﻿Type.registerNamespace("TrackMe.Mapping");

TrackMe.IComponent = function() {
};
TrackMe.IComponent.prototype = {
    getType: function() {}
};
TrackMe.IComponent.registerInterface('TrackMe.IComponent');

TrackMe.Mapping.createMap = function(mapid, provider, mapType, mapMode, latitude, longitude, zoom, datasource, options) {
  if ($get(mapid) == null) {
    return;
  }
  
  var map;
  zoom = options.zm == -1 ? 5 : options.zm;
  if (options.mp === "live") {
    if (options.mm === "3D") {
	  var threeDSettings = {
	    "heading": options.mvh,
	    "pitch": options.mvp,
	    "altitude": options.mva
	  }
	}
    map = new TrackMe.Mapping.LMap(mapid, options.mp, options.mt, options.mm, new TrackMe.Mapping.SLatLng(options.lt, options.lg), options.zm, threeDSettings, options);
    if (typeof(options.dashboardSize) !== "undefined") {
      map.showDashboardPanel();
    }
  } else {
    map = new TrackMe.Mapping.GMap(mapid, options.mp, options.mt, options.mm, new TrackMe.Mapping.SLatLng(options.lt, options.lg), options.zm);
    if (typeof(options.dashboardSize) !== "undefined") {
      map.showDashboardPanel({"size":options.dashboardSize});
    }
  }

  Sys.Application.addComponent(map);

  if (datasource != null) {
    map.showLoadingPanel();
  }
  
  var autoZoom = options.zm == -1 ? true : false;

  setTimeout(function() {
	  if (options.dsid != null) {
        var ds = eval(options.dsid);
        if (ds != null) {
          map.dataBind(ds, autoZoom);
        }
        map.hideLoadingPanel();
	  }
	}, 10);
	
	return map;
};

TrackMe.Mapping.SLatLng = function(lat, lng) {
  TrackMe.Mapping.SLatLng.initializeBase(this);
  this._latitude = lat;
  this._longitude = lng;
};

TrackMe.Mapping.SLatLng.prototype = {
  getLatitude: function() {
    return this._latitude;
  },
  setLatitude: function(lat) {
    this._latitude = lat;
  },
  
  getLongitude: function() {
    return this._longitude;
  },
  setLongitude: function(lng) {
    this._longitude = lng;
  },
  
  dispose: function() {
    this._latitude = null;
    this._longitude = null;
  },
  
  distanceFrom: function(latlng) {
    var lat1 = this.getLatitude();
    var lat2 = latlng.getLatitude();
    var lng1 = this.getLongitude();
    var lng2 = latlng.getLongitude();
    var R = 6371; // km
    return Math.acos(Math.sin(lat1)*Math.sin(lat2) + 
                      Math.cos(lat1)*Math.cos(lat2) *
                      Math.cos(lng2-lng1)) * R;
  },
  
  toString: function() {
    return this._latitude + ", " + this._longitude;
  },
  
  getType: function() {
    return TrackMe.Mapping.SLatLng.getName();
  }
};
TrackMe.Mapping.SLatLng.inheritsFrom(Sys.Component);
TrackMe.Mapping.SLatLng.registerClass('TrackMe.Mapping.SLatLng', Sys.Component, TrackMe.IComponent);

var $SLatLng = TrackMe.Mapping.createSLatLng = function TrackMe$Mapping$createSLatLng(lat, lng, ext) {
  var o = new TrackMe.Mapping.SLatLng(lat, lng);
  o.ext = ext;
  
  return o;
}
TrackMe.Mapping.SDetailLatLng = function(lat, lng, time, speed, distance) {
  TrackMe.Mapping.SDetailLatLng.initializeBase(this, [lat, lng]);

  this._time = time;
  
  if (speed === null) {
    this._speed = 0;
  } else {
    this._speed = speed;
  }
  
  if (distance === null) {
    this._distance = 0;
  } else {
    this._distance = distance;
  }
};

TrackMe.Mapping.SDetailLatLng.prototype = {
  getTime: function() {
    return this._time;
  },
  setTime: function(time) {
    this._time = time;
  },
  
  getSpeed: function() {
    return this._speed;
  },
  setSpeed: function(speed) {
    this._speed = speed;
  },
  
  getDistance: function() {
    return this._distance;
  },
  setDistance: function(distance) {
    this._distance = distance;
  },

  dispose: function() {
    this._time = null;
    this._speed = null;
    this._distance = null;
    TrackMe.Mapping.SDetailLatLng.callBaseMethod(this, 'dispose');
  },
  
  toString: function() {
    return TrackMe.Mapping.SDetailLatLng.callBaseMethod(this, 'toString');
  },
  
  getType: function() {
    return TrackMe.Mapping.SDetailLatLng.getName();
  }
};
TrackMe.Mapping.SDetailLatLng.inheritsFrom(TrackMe.Mapping.SLatLng);
TrackMe.Mapping.SDetailLatLng.registerClass('TrackMe.Mapping.SDetailLatLng', TrackMe.Mapping.SLatLng);

var $SDetailLatLng = TrackMe.Mapping.createSDetailLatLng = function TrackMe$Mapping$createSDetailLatLng(lat, lng, time, speed, distance) {
  return new TrackMe.Mapping.SDetailLatLng(lat, lng, time, speed, distance);
}

TrackMe.Mapping.SPoint = function(id, latlng, icon) {
  this._id = id;
  this._latlng = latlng;
  this._icon = typeof(icon) === "undefined" ? new TrackMe.Mapping.SIcon() : icon;
  this._map = null;
  this._wrappedPoint = null;
  this._visible = true;
};

TrackMe.Mapping.SPoint.prototype = {
  getId: function() {
    return this._id;
  },
  
  getLatLng: function() {
    return this._latlng;
  },
  setLatLng: function(latlng) {
    if (latlng.getLatitude() !== this._latlng.getLatitude() || latlng.getLongitude() !== this._latlng.getLongitude()) {
      this._latlng = latlng;
      if (this._map !== null) {
        this._map.movePoint(this);
      }
    }
  },
  
  getIcon: function() {
    return this._icon;
  },
  setIcon: function(icon) {
    this._icon = icon;
    if (this._map != null) {
      this._map.changePointIcon(this, icon);
    }
  },
  
  setMap: function(map) {
    this._map = map;
  },
  
  hide: function() {
    if (this._map !== null && this.isVisible() === true) {
      this._map.hidePoint(this);
      this._visible = false;
    }
  },
  show: function() {
    if (this._map !== null && this.isVisible() === false) {
      this._map.showPoint(this);
      this._visible = true;
    }
  },
  
  setWrappedPoint: function(point) {
    this._wrappedPoint = point;
  },
  getWrappedPoint: function() {
    return this._wrappedPoint;
  },
  
  isVisible: function() {
    return this._visible;
  },
  
  dispose: function() {
    this._id = null;
    this._latlng.dispose();
    this._latlng = null;
    this._icon = null;
    this._wrappedPoint = null;
    this._visible = null;
  },
  
  getType: function() {
    return TrackMe.Mapping.SPoint.getName();
  }
};
TrackMe.Mapping.SPoint.inheritsFrom(Sys.Component);
TrackMe.Mapping.SPoint.registerClass('TrackMe.Mapping.SPoint', Sys.Component, TrackMe.IComponent);

TrackMe.Mapping.SDetailPoint = function(id, latlng, title, details, icon) {
  TrackMe.Mapping.SDetailPoint.initializeBase(this, [id, latlng, icon]);
  this._title = title;
  this._details = details;
  this._bubbleEnabled = true;
};

TrackMe.Mapping.SDetailPoint.prototype = {
  getTitle: function() {
    return this._title;
  },
  setTitle: function(title) {
    this._title = title;
  },

  getDetails: function() {
    return this._details;
  },
  setDetails: function(details) {
    this._details = details;
  },
  
  setLatLng: function(latlng) {
    TrackMe.Mapping.SDetailPoint.callBaseMethod(this, 'setLatLng', [latlng]);
  },
  
  hide: function() {
    if (this._map.getBubblePoint() != null) {
      if (this._map.getBubblePoint().getId() === this.getId()) {
        this.hideBubble();
      }
    }
    
    TrackMe.Mapping.SDetailPoint.callBaseMethod(this, 'hide');
  },
  
  showBubble: function() {
    if (this._bubbleEnabled) {
      if (this._map !== null) {
        this._map.showBubble(this);
      }
    }
  },
  hideBubble: function() {
    if (this._map !== null) {
      this._map.hideBubble(this);
    }
    this._bubbleVisible = false;
  },
  disableBubble: function() {
    this.hideBubble();
    this._bubbleEnabled = false;
  },
  enableBubble: function() {
    this._bubbleEnabled = true;
  },
  isBubbleEnabled: function() {
    return this._bubbleEnabled;
  },

  dispose: function() {
    this._title = null;
    this._details = null;
    this._bubbleEnabled = null;
    TrackMe.Mapping.SDetailPoint.callBaseMethod(this, 'dispose');
  },
  
  getType: function() {
    return TrackMe.Mapping.SDetailPoint.getName();
  }
};
TrackMe.Mapping.SDetailPoint.inheritsFrom(TrackMe.Mapping.SPoint);
TrackMe.Mapping.SDetailPoint.registerClass('TrackMe.Mapping.SDetailPoint', TrackMe.Mapping.SPoint);


TrackMe.Mapping.SDoerPoint = function(id, latlng, opts) {
  TrackMe.Mapping.SDoerPoint.initializeBase(this, [id, latlng]);
  var iconUrl = "/ui/icons/doer-orange.gif";
  if (typeof(opts) !== "undefined" && opts !== null) {
    if (typeof(opts.iconUrl) !== "undefined" && opts.iconUrl !== null) {
      iconUrl = opts.iconUrl;
    }
  }
  this._icon = new TrackMe.Mapping.SIcon(id + "_ic", iconUrl, 
    new TrackMe.Mapping.SSize(16, 22), 
    new TrackMe.Mapping.SPixel(8, 16), null, null, 
    new TrackMe.Mapping.SPixel(8, 16));
  this._visible = true;
};

TrackMe.Mapping.SDoerPoint.prototype = {
  show: function() {
    if (this._map !== null && this.isVisible() === false) {
      this._map.showDoer(this);
      this._visible = true;
    }
  },
  
  showBubble: function() {
    if (this._bubbleEnabled) {
      if (this._map !== null) {
        this._map.showDoerBubble(this);
      }
    }
  },
  
  animate: function(lap) {
    if (this._map != null) {
      this._map.animatePoint(this, lap);
    }
  },
  
  dispose: function() {
    this.doerLocationInfoComplete = null;
    this._visible = null;
    TrackMe.Mapping.SDoerPoint.callBaseMethod(this, 'dispose');
  },
  
  getType: function() {
    return TrackMe.Mapping.SDoerPoint.getName();
  }
};
TrackMe.Mapping.SDoerPoint.inheritsFrom(TrackMe.Mapping.SDetailPoint);
TrackMe.Mapping.SDoerPoint.registerClass('TrackMe.Mapping.SDoerPoint', TrackMe.Mapping.SDetailPoint);


TrackMe.Mapping.SPhotoPoint = function(id, latlng, title) {
  TrackMe.Mapping.SPhotoPoint.initializeBase(this, [id, latlng, title]);
  this._icon = new TrackMe.Mapping.SIcon(id + "_ic", "/ui/sportsdo/photo.png", 
    new TrackMe.Mapping.SSize(26, 26), 
    new TrackMe.Mapping.SPixel(13, 13), null, null, 
    new TrackMe.Mapping.SPixel(15, 12));
  this._visible = true;
};

TrackMe.Mapping.SPhotoPoint.prototype = {
  show: function() {
    if (this._map !== null && this.isVisible() === false) {
      this._map.showPhoto(this);
      this._visible = true;
    }
  },
  
  showBubble: function() {
    if (this._bubbleEnabled) {
      if (this._map !== null) {
        this._map.showPhotoBubble(this);
      }
    }
  },
  
  dispose: function() {
    this._visible = null;
    TrackMe.Mapping.SPhotoPoint.callBaseMethod(this, 'dispose');
  },
  
  getType: function() {
    return TrackMe.Mapping.SPhotoPoint.getName();
  }
};
TrackMe.Mapping.SPhotoPoint.inheritsFrom(TrackMe.Mapping.SDetailPoint);
TrackMe.Mapping.SPhotoPoint.registerClass('TrackMe.Mapping.SPhotoPoint', TrackMe.Mapping.SDetailPoint);


TrackMe.Mapping.AnimationActor = function(point, laps) {
  this._point = point;
  this._lapPoints = [];
  
  for (var i=0;i<laps.length; i++) {
    Array.addRange(this._lapPoints, laps[i].getPoints());
  }
  
  var lapPoint = this._lapPoints[0];
  var data = {
    "speed": lapPoint.getSpeed(),
    "time": lapPoint.getTime(),
    "distance": lapPoint.getDistance(),
    "latlng": new TrackMe.Mapping.SLatLng(lapPoint.getLatitude(), lapPoint.getLongitude())
  };
  this._currentPositionInfo = data;
};

TrackMe.Mapping.AnimationActor.prototype = {
  getPoint: function() {
    return this._point;
  },
  
  getLapPoints: function() {
    return this._lapPoints;
  },
  
  getCurrentPositionInfo: function() {
    return this._currentPositionInfo;
  },
  setCurrentPositionInfo: function(info) {
    this._currentPositionInfo = info;
  },
  
  getType: function() {
    return TrackMe.Mapping.AnimationActor.getName();
  }
};
TrackMe.Mapping.AnimationActor.inheritsFrom(Sys.Component);
TrackMe.Mapping.AnimationActor.registerClass('TrackMe.Mapping.AnimationActor', Sys.Component);


TrackMe.Mapping.SIcon = function(id, imageUrl, imageSize, iconAnchor, shadowUrl, shadowSize, infoWindowAnchor) {
  TrackMe.Mapping.SIcon.initializeBase(this);
  this._id = id;
  this._imageUrl = imageUrl;
  this._shadowUrl = shadowUrl;
  
  if (imageSize !== null) {
    this._imageSize = imageSize;
  } else {
    this._imageSize = new TrackMe.Mapping.SSize(16, 16);
  }
  
  this._shadowSize = shadowSize;
  
  if (iconAnchor !== null) {
    this._iconAnchor = iconAnchor;
  } else {
    this._iconAnchor = new TrackMe.Mapping.SPixel(this._imageSize.getWidth() / 2, this._imageSize.getHeight());
  }

  this._infoWindowAnchor = infoWindowAnchor;
};

TrackMe.Mapping.SIcon.prototype = {
  getId: function() {
    return this._id;
  },
  
  getImageUrl: function() {
    return this._imageUrl;
  },
  setImageUrl: function(imageUrl) {
    this._imageUrl = imageUrl;
  },
  
  getShadowUrl: function() {
    return this._shadowUrl;
  },
  setShadowUrl: function(shadowUrl) {
    this._shadowUrl = shadowUrl;
  },
  
  getImageSize: function() {
    return this._imageSize;
  },
  setImageSize: function(imageSize) {
    if (imageSize.getType != null && imageSize.getType() === TrackMe.Mapping.SSize.getName()) {
      this._imageSize = imageSize;
    } else {
      throw "parameter 1 must be of type " + TrackMe.Mapping.SSize.getName();
    }
  },
  
  getShadowSize: function() {
    return this._shadowSize;
  },
  setShadowSize: function(size) {
    this._shadowSize = size;
  },
  
  getIconAnchor: function() {
    return this._iconAnchor;
  },
  setIconAnchor: function(point) {
    this._iconAnchor = point;
  },
  
  getInfoWindowAnchor: function() {
    return this._infoWindowAnchor;
  },
  setInfoWindowAnchor: function(point) {
    this._infoWindowAnchor = point;
  },
  
  dispose: function() {
    this._id = null;
    this._imageUrl = null;
    this._shadowUrl = null;
    if (this._imageSize != null) {
        this._imageSize.dispose();
        this._imageSize = null;
    }
    if (this._shadowSize != null) {
        this._shadowSize.dispose();
        this._shadowSize = null;
    }
    if (this._iconAnchor != null) {
        this._iconAnchor.dispose();
        this._iconAnchor = null;
    }
    this._infoWindowAnchor = null;
  },
  
  getType: function() {
    return TrackMe.Mapping.SIcon.getName();
  }
};
TrackMe.Mapping.SIcon.inheritsFrom(Sys.Component);
TrackMe.Mapping.SIcon.registerClass('TrackMe.Mapping.SIcon', Sys.Component, TrackMe.IComponent);


TrackMe.Mapping.SDefaultIcon = function(id, imageUrl) {
  TrackMe.Mapping.SDefaultIcon.initializeBase(this, [id, imageUrl, new TrackMe.Mapping.SSize(24, 24), new TrackMe.Mapping.SPixel(12, 24), null, null, new TrackMe.Mapping.SPixel(6, 12)]);
}
TrackMe.Mapping.SDefaultIcon.inheritsFrom(TrackMe.Mapping.SIcon);
TrackMe.Mapping.SDefaultIcon.registerClass('TrackMe.Mapping.SDefaultIcon', TrackMe.Mapping.SIcon);


TrackMe.Mapping.SSmallIcon = function(id, imageUrl) {
  TrackMe.Mapping.SSmallIcon.initializeBase(this, [id, imageUrl, new TrackMe.Mapping.SSize(16, 16), new TrackMe.Mapping.SPixel(8, 8), null, null, new TrackMe.Mapping.SPixel(8, 8)]);
}
TrackMe.Mapping.SSmallIcon.inheritsFrom(TrackMe.Mapping.SIcon);
TrackMe.Mapping.SSmallIcon.registerClass('TrackMe.Mapping.SSmallIcon', TrackMe.Mapping.SIcon);


TrackMe.Mapping.SFlagIcon = function(id, imageUrl) {
  TrackMe.Mapping.SFlagIcon.initializeBase(this, [id, imageUrl, new TrackMe.Mapping.SSize(18, 32), new TrackMe.Mapping.SPixel(0, 32), null, null, new TrackMe.Mapping.SPixel(6, 32)]);
}
TrackMe.Mapping.SFlagIcon.inheritsFrom(TrackMe.Mapping.SIcon);
TrackMe.Mapping.SFlagIcon.registerClass('TrackMe.Mapping.SFlagIcon', TrackMe.Mapping.SIcon);


TrackMe.Mapping.SMarkerIcon = function(id, imageUrl) {
  TrackMe.Mapping.SMarkerIcon.initializeBase(this, [id, imageUrl, new TrackMe.Mapping.SSize(18, 32), new TrackMe.Mapping.SPixel(9, 32), null, null, new TrackMe.Mapping.SPixel(6, 32)]);
}
TrackMe.Mapping.SMarkerIcon.inheritsFrom(TrackMe.Mapping.SIcon);
TrackMe.Mapping.SMarkerIcon.registerClass('TrackMe.Mapping.SMarkerIcon', TrackMe.Mapping.SIcon);


TrackMe.Mapping.SLandmarkIcon = function(id, imageUrl) {
  TrackMe.Mapping.SLandmarkIcon.initializeBase(this, [id, imageUrl, new TrackMe.Mapping.SSize(24, 24), new TrackMe.Mapping.SPixel(12, 24), null, null, new TrackMe.Mapping.SPixel(9, 24)]);
}
TrackMe.Mapping.SLandmarkIcon.inheritsFrom(TrackMe.Mapping.SIcon);
TrackMe.Mapping.SLandmarkIcon.registerClass('TrackMe.Mapping.SLandmarkIcon', TrackMe.Mapping.SIcon);


TrackMe.Mapping.SSize = function(width, height) {
  TrackMe.Mapping.SSize.initializeBase(this);
  this._width = width;
  this._height = height;
};

TrackMe.Mapping.SSize.prototype = {
  getWidth: function() {
    return this._width;
  },
  setWidth: function(width) {
    this._width = width;
  },
  
  getHeight: function() {
    return this._height;
  },
  setHeight: function(height) {
    this._height = height;
  },
  
  dispose: function() {
    this._width = null;
    this._height = null;
  },
  
  getType: function() {
    return TrackMe.Mapping.SSize.getName();
  }
};
TrackMe.Mapping.SSize.inheritsFrom(Sys.Component);
TrackMe.Mapping.SSize.registerClass('TrackMe.Mapping.SSize', Sys.Component, TrackMe.IComponent);


TrackMe.Mapping.SPixel = function(x, y) {
  TrackMe.Mapping.SPixel.initializeBase(this);
  this._x = x;
  this._y = y;
};

TrackMe.Mapping.SPixel.prototype = {
  getX: function() {
    return this._x;
  },
  setX: function(x) {
    this._x = x;
  },
  
  getY: function() {
    return this._y;
  },
  setY: function(y) {
    this._y = y;
  },
  
  dispose: function() {
    this._x = null;
    this._y = null;
  },
  
  getType: function() {
    return TrackMe.Mapping.SPixel.getName();
  }
};
TrackMe.Mapping.SPixel.inheritsFrom(Sys.Component);
TrackMe.Mapping.SPixel.registerClass('TrackMe.Mapping.SPixel', Sys.Component, TrackMe.IComponent);


TrackMe.Mapping.SPolyLine = function(id, latlngs, color, width, name) {
  this._id = id;
  this._latlngs = latlngs;
  this._color = color;
  this._width = width;
  this._name = name;
  this._originalColor = color;
  this._originalWidth = width;
  this._highlighted = false;
  this._wrappedPolyLine = null;
};

TrackMe.Mapping.SPolyLine.prototype = {
  getId: function() {
    return this._id;
  },
  
  getLatLngs: function() {
    return this._latlngs;
  },
  
  getName: function() {
    return this._name;
  },
  
  getColor: function() {
    return this._color;
  },
  
  getWidth: function() {
    return this._width;
  },
  
  setWrappedPolyLine: function(polyline) {
    this._wrappedPolyLine = polyline;
  },
  getWrappedPolyLine: function() {
    return this._wrappedPolyLine;
  },
  
  highlight: function(map) {
    if (this.isHighlighted() == false) {
      map.removePolyLine(this);
      this.width = this.width + 3;
      this.color = this.color;
      this._highlighted = true;
      map.addPolyLine(this);
    }
  },
  unhighlight: function(map) {
    if (this.isHighlighted() == true) {
      map.removePolyLine(this);
      this.width = this._originalWidth;
      this.color = this._originalColor;
      this._highlighted = false;
      map.addPolyLine(this);
    }
  },
  isHighlighted: function() {
    return this._highlighted;
  },
  
  getType: function() {
    return TrackMe.Mapping.SPolyLine().getName();
  }
};
TrackMe.Mapping.SPolyLine.inheritsFrom(Sys.Component);
TrackMe.Mapping.SPolyLine.registerClass('TrackMe.Mapping.SPolyLine', Sys.Component, TrackMe.IComponent);


TrackMe.Mapping.SRoute = function(id, points) {
  this._id = id;
  this._points = points;
  this._polylines = new Hash();
  this._hidden = false;
  this._map = null;
};

TrackMe.Mapping.SRoute.prototype = {
  getId: function() {
    return this._id;
  },
  
  getPoints: function() {
    return this._points;
  },
  
  getPolyLines: function() {
    return this._polylines;
  },
  
  addPolyLine: function(polyline) {
    polyline.parent = this;
    this._polylines.setItem(polyline.getId(), polyline);
  },
  
  hidePolyLine: function(id) {
    var p = this._polylines.getItem(id);
    if (p != null) {
      this._map.removePolyLine(p);
    //this._polylines.removeItem(id);
    }
  },
  
  addPoints: function(points, draw) {
    var previousPoint = this._points[this._points.length-1];
    Array.addRange(this._points, points);
    
    var p = [previousPoint];
    Array.addRange(p, points);
    if (p.length > 1 && draw === true) {
      var polyline = new TrackMe.Mapping.SPolyLine(this.getId() + "_" + this._polylines.keys.length, p, "#ff0000", 1);
      this._map.addPolyLine(polyline);
      this._polylines.setItem(polyline.getId(), polyline);
    }
  },
  
  setOwnerMap: function(map) {
    this._map = map;
  },
  getOwnerMap: function() {
    return this._map;
  },
  
  show: function() {
    // show all polylines
    if (this._hidden === true) {
      var plines = this.getPolyLines();
      var key, item;
      for (var i=0; i<plines.keys.length; i++) {
         key = plines.keys[i];
         item = plines.getItem(key);
         this._map.addPolyLine(item);
      }
      this._hidden = false;
      this._map._routeVisibilityChangedHandler({"route":this});
    } else {
      var plines = this.getPolyLines();
      var key, item;
      for (var i=0; i<plines.keys.length; i++) {
         key = plines.keys[i];
         item = plines.getItem(key);
         if (item.getWrappedPolyLine() == null) {
           this._map.addPolyLine(item);
         }
      }
    }
  },
  hide: function() {
    // hide all polylines
    if (this._hidden === false) {
      var plines = this.getPolyLines();
      var key, item;
      for (var i=0; i<plines.keys.length; i++) {
         key = plines.keys[i];
         item = plines.getItem(key);
         this._map.removePolyLine(item);
      }
      this._hidden = true;
      this._map._routeVisibilityChangedHandler({"route":this});
    }
  },
  toggleVisibility: function() {
    if (this.isHidden() === true) {
      this.show();
    } else {
      this.hide();
    }
  },
  isHidden: function() {
    return this._hidden;
  },

  getType: function() {
    return TrackMe.Mapping.SRoute.getName();
  }
};
TrackMe.Mapping.SRoute.inheritsFrom(Sys.Component);
TrackMe.Mapping.SRoute.registerClass('TrackMe.Mapping.SRoute', Sys.Component, TrackMe.IComponent);


TrackMe.Mapping.SLap = function(id, points) {
  TrackMe.Mapping.SLap.initializeBase(this, [id, points]);
  
  if (this.getPoints().length > 0) {
    this._startTime = this.getPoints()[0].getTime();
    this._endTime = this.getPoints()[this.getPoints().length - 1].getTime();
  }
  
  //  limit amount displayed
  this.minDisplayPosition = this._startTime;
  if (this.getPoints().length > 100) {
    this.maxDisplayPosition = this.getPoints()[100].getTime();
  } else {
    this.maxDisplayPosition = this._endTime;
  }
};

TrackMe.Mapping.SLap.prototype = {
  getStartTime: function() {
    return this._startTime;
  },
  getEndTime: function() {
    return this._endTime;
  },

  getMaxTime: function() {
    return this._maxTime;
  },
  
  getType: function() {
    return TrackMe.Mapping.SLap.getName();
  }
};
TrackMe.Mapping.SLap.inheritsFrom(TrackMe.Mapping.SRoute);
TrackMe.Mapping.SLap.registerClass('TrackMe.Mapping.SLap', TrackMe.Mapping.SRoute);


TrackMe.Mapping.SPanelControl = function(id, div, location, offset, opts) {
  this._id = id;
  this._div = div;
  this._offset = offset;
  this._location = location;

  if (typeof(opts) !== "undefined" && opts !== null && opts.visible == true) {
    this.hide();
  }
};

TrackMe.Mapping.SPanelControl.prototype = {
  getId: function() {
    return this._id;
  },
  
  getDivObject: function() {
    return this._div;
  },
  
  getOffset: function() {
    return this._offset;
  },
  
  getLocation: function() {
    return this._location;
  },
  
  hide: function() {
    this._div.style.display = "none";
  },
  show: function() {
    this._div.style.display = "block";
  },
  
  getType: function() {
    return TrackMe.Mapping.SPanelControl.getName();
  }
};
TrackMe.Mapping.SPanelControl.inheritsFrom(Sys.Component);
TrackMe.Mapping.SPanelControl.registerClass('TrackMe.Mapping.SPanelControl', Sys.Component, TrackMe.IComponent);


TrackMe.Mapping.SMap = function(id, provider, mapType, mapMode, center, zoom) {
  TrackMe.Mapping.SMap.initializeBase(this);
  if (mapMode == null) { mapMode = '2D'; }
  if (mapType == null) { mapType = 'Normal'; }

  this._id = id;
  this._provider = provider;
  this._panelControls = [];
  this._points = new Hash();
  this._doers = new Hash();
  this._photos = new Hash();
  this._routes = new Hash();
  this._polylines = new Hash();
  this._originalCenter = center;
  this._map = null;
  this._enableAddingPoints = true;
  this._animatedPoints = [];
  this._animateTimer = null;
  this._animationTickDelegate = null;
  this._bubbledPoint = null;
  this._enableBubble = true;
  this._animationActorCompleteDelegate = null;
  this._animationCompleteDelegate = null;
  this._onReplayStartDelegate = null;
  this._onReplayStopDelegate = null;
  this._onReplayPauseDelegate = null;
  this._onReplayFastForwardDelegate = null;
  this._onReplayRewindDelegate = null;
  
  this._onZoomedDelegate = null;
  if (this._onZoomedDelegate === null) {
    this._onZoomedDelegate = Function.createDelegate(this, this._onZoomedHandler);
  }

  this._onMapDataboundDelegate = null;
  if (this._onMapDataboundDelegate === null) {
    this._onMapDataboundDelegate = Function.createDelegate(this, this._onMapDataboundHandler);
  }
  
  this._onPointClickDelegate = null;
  if (this._onPointClickDelegate === null) {
    this._onPointClickDelegate = Function.createDelegate(this, this._onPointClickHandler);
  }
  
  this._onMapClickDelegate = null;
  if (this._onMapClickDelegate === null) {
    this._onMapClickDelegate = Function.createDelegate(this, this._onMapClickHandler);
  }
  
  this._routeVisibilityChangedDelegate = null;
  if (this._routeVisibilityChangedDelegate === null) {
    this._routeVisibilityChangedDelegate = Function.createDelegate(this, this._routeVisibilityChangedHandler);
  }
  
  this._onPointAddDelegate = null;
  if (this._onPointAddDelegate === null) {
    this._onPointAddDelegate = Function.createDelegate(this, this._onPointAddHandler);
  }
  
  this._onPointMovedDelegate = null;
  if (this._onPointMovedDelegate === null) {
    this._onPointMovedDelegate = Function.createDelegate(this, this._onPointMovedHandler);
  }
  
  this._onDoerAddDelegate = null;
  if (this._onDoerAddDelegate === null) {
    this._onDoerAddDelegate = Function.createDelegate(this, this._onDoerAddHandler);
  }
  
  this._preDoerBubbleShowDelegate = null;
  if (this._preDoerBubbleShowDelegate === null) {
    this._preDoerBubbleShowDelegate = Function.createDelegate(this, this._preDoerBubbleShowHandler);
  }
  
  this._prePointBubbleShowDelegate = null;
  if (this._prePointBubbleShowDelegate === null) {
    this._prePointBubbleShowDelegate = Function.createDelegate(this, this._prePointBubbleShowHandler);
  }
  
  this._onPointBubbleShowDelegate = null;
  if (this._onPointBubbleShowDelegate === null) {
    this._onPointBubbleShowDelegate = Function.createDelegate(this, this._onPointBubbleShowHandler);
  }
  
  this._onMapChangeViewDelegate = null;
  if (this._onMapChangeViewDelegate === null) {
    this._onMapChangeViewDelegate = Function.createDelegate(this, this._onMapChangeViewHandler);
  }
  
  this._prePointBubbleCloseDelegate = null;
  if (this._prePointBubbleCloseDelegate === null) {
    this._prePointBubbleCloseDelegate = Function.createDelegate(this, this._prePointBubbleCloseHandler);
  }
  
  var instance = this; 
  if (window.attachEvent) {
    window.attachEvent("onunload", function(evnt) {
      instance.dispose(evnt)
    });
  } else {
    window.addEventListener("unload", function(evnt) {
      instance.dispose(evnt)
    } , false);
  }
};

TrackMe.Mapping.SMap.prototype = {
  getWrappedMap: function() {
    return this._map;
  },
  
  getId: function() {
    return this._id;
  },
  
  findAddress: function(address, callback) {
  },
  
  getPermaLinkQueryString: function(keys) {
    var sb = new Sys.StringBuilder();
    sb.append("zm=");
    sb.append(this.getCurrentZoom());
    sb.append("&mp=");
    sb.append(this.getMapProvider());
    sb.append("&mt=");
    sb.append(this.getMapType());
    var mm = this.getMapMode();
    if (mm != null) {
      sb.append("&mm=");
      sb.append(mm);
      if (mm === "3D") {
        sb.append("&mva=");
        sb.append(this.getAltitude());
        sb.append("&mvh=");
        sb.append(this.getHeading());
        sb.append("&mvp=");
        sb.append(this.getPitch());
      }
    }
    var mapCenter = this.getMapCenter();
    sb.append("&mc=");
    sb.append(mapCenter.getLatitude() + "," + mapCenter.getLongitude());
    
    if (typeof(keys) !== "undefined" && keys !== null) {
      for (var i=0; i<keys.length; i++) {
        var key = keys[i];
        sb.append("&");
        var value = Request.queryString(key);
        if (value != null) {
          sb.append(key);
          sb.append("=");
          sb.append(value);
        }
      }
    }

    return sb.toString();
  },

  getPermaLink: function(keys) {
    return Request.currentUrlWithoutQueryString + "?" + this.getPermaLinkQueryString(keys);
  },
  
  throwEvent: function(ev, args) {
    var h = this.get_events().getHandler(ev);
    if (h) {
      h(this, args);
    }
  },
  
  totalDistance: function(latlngs) {},
  
  OnChangeView: function() {
    var v = this.getViewRegion();
    //this.togglePointsInRegion(this.getDoers(), v);
    //this.togglePointsInRegion(this.getPoints(), v);
    //this.togglePointsInRegion(this.getPhotos(), v);
    
    if (typeof(this._blockChangeViewEvent) === "undefined" || this._blockChangeViewEvent === false) {
      this._onMapChangeViewHandler();
    } else {
      this._blockChangeViewEvent = false;
    }
  },
  _onMapChangeViewHandler: function(args) {
    var h = this.get_events().getHandler('onchangeview');
    if (h) {
      h(this, args);
    }
  },
  addOnMapChangeView: function(handler) {
    this.get_events().addHandler("onchangeview", handler);
  },
  removeOnMapChangeView: function(handler) {
    this.get_events().removeHandler("onchangeview", handler);
  },
  _onZoomedHandler: function(args) {
    this.throwEvent('onzoomed', args);
  },
  addOnMapZoomed: function(handler) {
    this.get_events().addHandler("onzoomed", handler);
  },
  removeOnMapZoomed: function(handler) {
    this.get_events().removeHandler("onzoomed", handler);
  },
  
  disableDoubleClickZoom: function() {},
  
  createRoute: function(route) {
    var points = route.pts;
    var latlngs = [];
    var pointsIndex = 0;
    var point;
    while (pointsIndex < points.length) {
      point = points[pointsIndex];
      
      if (route.isl) {
        Array.add(latlngs, new TrackMe.Mapping.SDetailLatLng(point.lt, point.lg, point.tm, point.s, point.d));
      } else {
        Array.add(latlngs, new TrackMe.Mapping.SLatLng(point.lt, point.lg));
      }
      
      pointsIndex++;
    }
    
    if (route.isl) {
      var r = new TrackMe.Mapping.SLap(route.id, latlngs);
      r.setOwnerMap(this);
      return r;
    } else {
      var r = new TrackMe.Mapping.SRoute(route.id, latlngs);
      r.setOwnerMap(this);
      return r;
    }
  },
  
  dataBind: function(data, autoFocus) {
    if (typeof(data) === "undefined" || data === null) {
      return;
    }
    
    var routes = data.Routes;
    var pois = data.PointsOfInterest;
    var photos = data.Photos;
    var doers = data.Doers;
    
    
    if (routes != null) {
      var routeIndex = 0;
      var route, sroute;
      while (routeIndex < routes.length) 
      {
        route = routes[routeIndex];
        sroute = this.createRoute(route);

        if (sroute.getType() === TrackMe.Mapping.SLap.getName()) 
        {
          this.addRoute(sroute);
          this.drawLap(sroute, 0, sroute.getEndTime());
        } 
        else 
        {
          this.addRoute(sroute);
          this.drawRoute(sroute, route.c, route.w);
        }
        routeIndex++;
      }
    }
    
    var instance = this;
    instance._dataBindPois(pois);
    
    
    if (photos != null) {
      var photoIndex = 0;
      var photo, photoPoint;
      while (photoIndex < photos.length) {
        photo = photos[photoIndex];
        photoPoint = new TrackMe.Mapping.SPhotoPoint(photo.id, new TrackMe.Mapping.SLatLng(photo.lt, photo.lg), photo.t);
        if (photo.ext != null) {
          photoPoint.ext = photo.ext;
        }
        this.addPhoto(photoPoint);
        photoIndex++;
      }
    }
    
    if (doers != null) {
      var doerIndex = 0;
      var doer, doerPoint;
      while (doerIndex < doers.length) {
        doer = doers[doerIndex];
        if(doer.pt != null)
        {
            var opts = {};
            if(doer.ic)
            {
                opts.iconUrl = doer.ic.ImageUrl;
            }
            var sLatLng = null;
            if(doer.pt)
            {
                sLatLng = new TrackMe.Mapping.SLatLng(doer.pt.lt, doer.pt.lg);
            }
            doerPoint = new TrackMe.Mapping.SDoerPoint(doer.nn, sLatLng, opts);
            if (doer.ext != null) {
              doerPoint.ext = doer.ext;
            }
            this.addDoer(doerPoint);
        }
        doerIndex++;
      }
    }
    var autofocused = false;
    if (typeof(autoFocus) !== "undefined" && autoFocus === true) 
    {
        var allPoints = this.getAllLatLngs();

        if (allPoints.length > 0) {
            this.focusView(allPoints);
            autofocused = true;
        }
    }

    var args = {"autofocused": autofocused};
    this._onMapDataboundDelegate(args);
  },
  
  getAllLatLngs: function() {
    var allPoints = [];
    var routes = this.getRoutes();
    for(var r=0; r < routes.length; r++)
    {
        var route = routes.getItem(routes.keys[r]);
        allPoints = allPoints.concat(route.getPoints());
    }
    
    var points = this.getPoints();
    for(var p=0; p < points.length; p++)
    {
        var point = points.getItem(points.keys[p]);
        allPoints.push(point.getLatLng());
    }
    
    var photos = this.getPhotos();
    for(var p=0; p < photos.length; p++)
    {
        var photo = photos.getItem(photos.keys[p]);
        allPoints.push(photo.getLatLng());
    }
    
    var doers = this.getDoers();
    for(var d=0; d < photos.length; d++)
    {
        var doer = doers.getItem(photos.keys[p]);
        allPoints.push(doer.getLatLng());
    }
    
    return allPoints;
  },
  
  _dataBindPois: function(pois) {
    if (pois != null) {
      var poiIndex = 0;
      var route, poi, points, latlngs, pointsIndex, point, pic;
      
      var liveShapes = [];
      var mapProvider = this.getMapProvider();
      
      while (poiIndex < pois.length) {
        poi = pois[poiIndex];
        points = pois[poiIndex].pts;
        
        latlngs = [];
        pointsIndex = 0;
        while (pointsIndex < points.length) {
          point = points[pointsIndex];
          Array.add(latlngs, new TrackMe.Mapping.SLatLng(point.lt, point.lg));
          pointsIndex++;
        }
        
        if (latlngs.length == 1) {
          if (poi.dtt == null && poi.dtd == null) {
            pic = new TrackMe.Mapping.SPoint(poi.id, latlngs[0], this.createIcon(poi.id, poi.iu, poi.it));
          } else {
            pic = new TrackMe.Mapping.SDetailPoint(poi.id, latlngs[0], poi.dtt, poi.dtd, this.createIcon(poi.id, poi.iu, poi.it));
          }
          
          if (mapProvider !== "live") {
            this.addPoint(pic);
          } else {
            var opts = { "supressAdd": true, "shape":null };
            if (this.addPoint(pic, opts) == true) {
              if (opts.shape != null) {
                Array.add(liveShapes, opts.shape);
              }
            }
          }
        } else {
          route = new TrackMe.Mapping.SRoute(poi.id, latlngs);
          route.setOwnerMap(this);
          this.addRoute(route);
          this.drawRoute(route, poi.c, poi.w);
          
          if (poi.iu != null && poi.iu.length > 0) {
            pic = new TrackMe.Mapping.SPoint(poi.id + '_ic', new TrackMe.Mapping.SLatLng(poi.ipt.lt, poi.ipt.lg), this.createIcon(poi.id, poi.iu, poi.it));
            
            if (mapProvider !== "live") {
              this.addPoint(pic);
            } else {
              var opts = { "supressAdd": true, "shape":null };
              if (this.addPoint(pic, opts) == true) {
                if (opts.shape != null) {
                  Array.add(liveShapes, opts.shape);
                }
              }
            }
          }
        }
        
        poiIndex++;
      }
      
      if (mapProvider === "live") {
        // VE6 allows shapes to be added from an array. bulkAddShapes falls back to adding 1 by 1 if adding array fails.
        this.bulkAddShapes(liveShapes);
      }
    }
  },
  
  createIcon: function(id, imageUrl, icontype) {
    if (icontype == 0) {
      return new TrackMe.Mapping.SMarkerIcon(id, imageUrl);
    } else if (icontype == 1) {
      return new TrackMe.Mapping.SDefaultIcon(id, imageUrl);
    } else if (icontype == 2) {
      return new TrackMe.Mapping.SFlagIcon(id, imageUrl);
    } else if (icontype == 3) {
      return new TrackMe.Mapping.SLandmarkIcon(id, imageUrl);
    } else {
      return new TrackMe.Mapping.SIcon(id + "_icon", imageUrl);
    }
  },
  
  convertIconClass: function(icon) {},

  convertLatLngClass: function(latlng) {},
  
  resetCenter: function() {
    this.setMapCenter(this._originalCenter);
  },
  
  getMapProvider: function() {
    return this._provider;
  },
  
  getMapType: function() {},
  setMapType: function(mapType) {},
  
  getMapMode: function() {},
  setMapMode: function(mode) {},
  
  getMapCenter: function() {},
  setMapCenter: function(latlng, zoom) {},

  moveToLatLng: function(latlng) {},

  resizeMap: function(width, height) {},
  getMapSize: function() {},
  
  getViewRegion: function() {},
  
  autoZoom: function(topRight, bottomLeft) {
    this._blockChangeViewEvent = true;
  },
  
  getCurrentZoom: function() {},
  
  setZoom: function(zoom) {},
  
  focusView: function(points) {
    if (points.length === 0) {
      this.setZoom(3);
      return;
    }
    
    var point;
    var maxLatitude = minLatitude = maxLongitude = minLongitude = null;
    
    for (var i=0; i<points.length; i++) {
      point = points[i];
      if (maxLatitude != null) {
        if (point.getLatitude() > maxLatitude) { maxLatitude = point.getLatitude(); }
        else if (point.getLatitude() < minLatitude) { minLatitude = point.getLatitude(); }
    
        if (point.getLongitude() > maxLongitude) { maxLongitude = point.getLongitude(); }
        else if (point.getLongitude() < minLongitude) { minLongitude = point.getLongitude(); }
      } else {
        maxLatitude = minLatitude = point.getLatitude();
        maxLongitude = minLongitude = point.getLongitude();
      }
    }
    
    var centerLatitude = ((maxLatitude - minLatitude) / 2) + minLatitude;
    var centerLongitude = ((maxLongitude - minLongitude) / 2) + minLongitude;
    this.setMapCenter(new TrackMe.Mapping.SLatLng(centerLatitude, centerLongitude));
    
    this.autoZoom(new TrackMe.Mapping.SLatLng(maxLatitude, maxLongitude), new TrackMe.Mapping.SLatLng(minLatitude, minLongitude));
  },
  
  setAltitude: function(altitude) {},
  setPitch: function(pitch) {},
  setHeading: function(heading) {},
  getAltitude: function() {},
  getPitch: function() {},
  getHeading: function() {},
  setMapView: function(latlng, altitude, pitch, heading) {},
  
  enableAddingPoints: function() {
    this._enableAddingPoints = true;
  },
  disableAddingPoints: function() {
    this._enableAddingPoints = false;
  },
  isAddingPointsEnabled: function() {
    return this._enableAddingPoints;
  },
  
  addPolyLine: function(polyline) {
    if (this.findPolyLineById(polyline.getId()) == null) {
      this._polylines.setItem(polyline.getId(), polyline);
      return true;
    } else {
      return false;
    }
  },
  removePolyLine: function(polyline) {
    this._polylines.removeItem(polyline.getId());
  },
  findPolyLineById: function(id) {
    return this._polylines.getItem(id);
  },
  getPolyLines: function() {
    return this._polylines;
  },
  
  getRoutes: function() {
    return this._routes;
  },
  
  _routeVisibilityChangedHandler: function(args) {
    var h = this.get_events().getHandler('routeVisibilityChanged');
    if (h) {
      h(this, args);
    }
  },
  addRouteVisibilityChanged: function(handler) {
    this.get_events().addHandler("routeVisibilityChanged", handler);
  },
  removeRouteVisibilityChanged: function(handler) {
    this.get_events().removeHandler("routeVisibilityChanged", handler);
  },
  addRoute: function(route, draw) {
    // only add the route if an id of the same doesnt already exist
    if (!this._routes.hasItem(route.getId())) {
      route.setOwnerMap(this);
      this._routes.setItem(route.getId(), route);

      if (route.getType() == TrackMe.Mapping.SLap.getName() && draw == true) {
        this.drawLap(route, 0, 100);
      } else if (draw == true) {
        this.drawRoute(route, "#ff0000", 1);
      }
      
      this._routeVisibilityChangedHandler({"route":route});
      return true;
    } else {
      return false;
    }
  },
  removeAllRoutes: function() {
    var route, routes;
    var index = 0;
    routes = this.getRoutes();
    while (routes.length > 0) {
      route = routes.getItem(routes.keys[0]);
      this.removeRoute(route);
    }
  },
  removeRoute: function(route) {
    var id = route.getId();
    this.hideRouteById(id);
    this._routes.removeItem(id)
  },
  removeRouteById: function(id) {
    this.hideRouteById(id);
    this._routes.removeItem(id)
  },
  drawRoute: function(route, color, width) {
    var polyline = new TrackMe.Mapping.SPolyLine(route.getId(), route.getPoints(), color, width);
    this.addPolyLine(polyline);
    route.addPolyLine(polyline);
  },
  hideRouteById: function(id) {
    var route = this._routes.getItem(id);
    if (route != null) {
      route.hide();
    }
    return route;
  },
  findRouteById: function(id) {
    return this._routes.getItem(id);
  },
  focusOnRoute: function(id) {
    var route = this.findRouteById(id);
    if (route != null) {
      this.focusView(route.getPoints());
    }
  },
  drawLap: function(route, startTime, endTime) {
    var points = route.getPoints();
    
    if (points.length>0) {
      var pos1 = -1, pos2 = -1;
      var currentPoint;
      for (var i=0;i<points.length;i++) {
        currentPoint = points[i];
        
        if (currentPoint.getTime()>=startTime && pos1<0) {
          pos1 = i;
          if (pos1>0 && currentPoint.getTime()>startTime) pos1--; // if overshot then step back
        }
        
        if (currentPoint.getTime() <= endTime) {
          pos2 = i;
        } else {
          break;
        }
      }
      
      if (pos2<points.length-1) pos2++;
      
      var newpts = points.slice(pos1, pos2 + 1);
      var polyline;
      //this.map.resetCentre(newpts);
      //this.map.clearOverlays();
      
      if (newpts.length==1) {
        Array.add(newpts, newpts[0]);
      }
      
      if (newpts.length>1) {
        if(1==0) {
          id = route.getId() + '-' + '0';
          polyline = new TrackMe.Mapping.SPolyLine(id, newpts, "#BB0000", 3);
          this.addPolyLine(polyline);
          route.addPolyLine(polyline);
        } else {
          // find out max speed
          var maxSpeed = 0;
          var newPoint;
          
          for (var i=0; i<newpts.length; i++) {
            newPoint = newpts[i];
            if (newPoint.getSpeed() > maxSpeed) maxSpeed = newPoint.getSpeed();
          }
          
          var color = ["#000000", "#1E90FF", "#00F000", "#F00000"];
          
          var sepSize = Math.ceil(maxSpeed / 5);
          var trackSpeed = [2*sepSize, 3*sepSize, 4*sepSize, 5*sepSize];
          var currentDivision = 0;
          var currentDivisionStartPos = 0;
          var speed, division, ln, lineId;
          
          for (var i=0; i<newpts.length; i++) {
            newPoint = newpts[i];
            speed = newPoint.getSpeed();
            division = 0;
            if (speed >= trackSpeed[0]) division = 1;
            if (speed >= trackSpeed[1]) division = 2;
            if (speed >= trackSpeed[2]) division = 3;
            
            if (division != currentDivision) {
              ln = newpts.slice(currentDivisionStartPos, i + 1);
              lineId = route.getId() + '-' + i;
              polyline = new TrackMe.Mapping.SPolyLine(lineId, ln, color[currentDivision], 3);
              this.addPolyLine(polyline);
              route.addPolyLine(polyline);
              currentDivision = division;
              currentDivisionStartPos = i;
            }
          }
          
          ln = newpts.slice(currentDivisionStartPos, newpts.length);
          
          lineId = route.getId() + '-' + 0;
          polyline = new TrackMe.Mapping.SPolyLine(lineId, ln, color[currentDivision], 3);
          this.addPolyLine(polyline);
          route.addPolyLine(polyline);
        }
      }
    }
  },
  
  findInArrayById: function(id, array) {
    var index = 0;
    while (index < array.length) {
      if (array[index].getId() == id) {
        return array[index];
      }
      index++;
    }
  },
  
  runAnimation: function(speed, startTime) {
    if (this._animationTickDelegate === null) {
      this._animationTickDelegate = Function.createDelegate(this, this._onAnimationTickHandler);
    }
    
    if (this._animationActorCompleteDelegate === null) {
      this._animationActorCompleteDelegate = Function.createDelegate(this, this._onAnimationActorCompleteHandler);
    }
    
    if (this._animationCompleteDelegate === null) {
      this._animationCompleteDelegate = Function.createDelegate(this, this._onAnimationCompleteHandler);
    }
    
    this._onReplayStartHandler();
    
    if (this._animatedPoints.length > 0 && this._animateTimer == null) {
      var instance = this;
      this._animateTime = startTime == null ? 0 : startTime;
      speed = speed == null ? 100 : speed;

      this.animateTimer_OnTick = function() {
        var numberAnimationsComplete = 0;
        var args = {
          actors: []
        };

        var actor, apoint, actorArgs;
        for (var i=0; i<instance._animatedPoints.length; i++) {
          actor = instance._animatedPoints[i];
          apoint = instance.getAnimatedPosition(actor.getLapPoints());
          if (apoint != null) {
            actor.setCurrentPositionInfo(apoint);
          }
          
          if (apoint != null) {
            apoint.point = actor.getPoint();
              
            if (apoint != null) {
              actor.getPoint().setLatLng(apoint.latlng);
              instance._animateTime += 2;
              Array.add(args.actors, apoint);
            } else {
              actorArgs = new Object();
              actorArgs.actor = actor;
              actorArgs.completeSecond = instance.getCurrentAnimationTime();
              actorArgs.removeActor = true;
              instance._animationActorCompleteDelegate(actorArgs);
              if (actorArgs.removeActor === true) {
                numberAnimationsComplete++;
              }
            }
          } else {
            numberAnimationsComplete++;
          }
        }
        
        args.time = instance.getCurrentAnimationTime();
        instance._animationTickDelegate(args);
        
        if (numberAnimationsComplete == instance._animatedPoints.length) {
          var cargs = {
            time: instance.getCurrentAnimationTime()
          };
          instance.stopAnimation();
          instance._animationCompleteDelegate(cargs);
        }
      };
      
      this._animateTimer = $create(TrackMe.Timer, {enabled:true,id:this.getId() + "_animateTimer",interval:speed}, {tick:this.animateTimer_OnTick}, null, null);
    }
  },
  stopAnimation: function() {
    if (this._animateTimer != null) {
      this._animateTimer.set_enabled(false);
      Sys.Application.removeComponent(this._animateTimer);
      this._animateTime = 0;
      this._animateTimer = null;
      this._onReplayStopHandler();
    }
  },
  pauseAnimation: function(value) {
    if (this._animateTimer != null) {
      this._animateTimer.set_enabled(!value);
      this._onReplayPauseHandler({"paused":value});
    }
  },
  isAnimationPaused: function() {
    if (this._animateTimer != null) {
      return !this._animateTimer.get_enabled();
    } else {
      return true;
    }
  },
  changeAnimationSpeed: function(speed) {
    speed = speed == null ? 100 : speed;
    
    if (this._animateTimer != null) {
      this.pauseAnimation(true);
      this._animateTimer.set_interval(speed);
      this.pauseAnimation(false);
    }
  },
  getCurrentAnimationTime: function() {
    return this._animateTime;
  },
  getAnimatedPosition: function(lapPoints) {
    var pos = -1;
          
    for (var i=1; i<lapPoints.length; i++) {
      if (lapPoints[i].getTime() > this._animateTime) {
        pos = i;
        break;
      }
    }

    if (pos < 0) {
      return null;
    } else {
      var lapPoint = lapPoints[pos];
      var prevLapPoint = lapPoints[pos - 1];
      
      var timeDiff = lapPoint.getTime() - prevLapPoint.getTime();
      var speedDiff = lapPoint.getSpeed() - prevLapPoint.getSpeed();
      var distanceDiff = lapPoint.getDistance() - prevLapPoint.getDistance();
      var latDiff = lapPoint.getLatitude() - prevLapPoint.getLatitude();
      var lngDiff = lapPoint.getLongitude() - prevLapPoint.getLongitude();
      var timeInto = this._animateTime - prevLapPoint.getTime();

      //var time = this.animateTime;
      //var speed = lapPoints[pos - 1].speed + speedDiff * timeInto/timeDiff;
      //var distance = lapPoints[pos - 1].distance + distanceDiff * timeInto/timeDiff;

      var data = {
        "speed": lapPoint.getSpeed(),
        "time": lapPoint.getTime(),
        "distance": lapPoint.getDistance(),
        "latlng": new TrackMe.Mapping.SLatLng(prevLapPoint.getLatitude() + latDiff * timeInto/timeDiff, prevLapPoint.getLongitude() + lngDiff * timeInto/timeDiff)
      };
      
      return data;
    }
  },
  animatePoint: function(point, laps) {
    Array.add(this._animatedPoints, new TrackMe.Mapping.AnimationActor(point, laps));
  },
  getAnimatedPoints: function() {
    return this._animatedPoints;
  },
  removeAllAnimationPoints: function() {
    Array.clear(this._animatedPoints);
  },
  _onAnimationTickHandler: function(args) {
    var h = this.get_events().getHandler('animationTick');
    if (h) {
      h(this, args);
    }
  },
  addOnAnimationTick: function(handler) {
    this.get_events().addHandler("animationTick", handler);
  },
  removeOnAnimationTick: function(handler) {
    this.get_events().removeHandler("animationTick", handler);
  },
  _onAnimationActorCompleteHandler: function(args) {
    var h = this.get_events().getHandler('animationActorComplete');
    if (h) {
      h(this, args);
    }
  },
  addOnAnimationActorComplete: function(handler) {
    this.get_events().addHandler("animationActorComplete", handler);
  },
  removeOnAnimationActorComplete: function(handler) {
    this.get_events().removeHandler("animationActorComplete", handler);
  },
  _onAnimationCompleteHandler: function(args) {
    var h = this.get_events().getHandler('animationComplete');
    if (h) {
      h(this, args);
    }
  },
  addOnAnimationComplete: function(handler) {
    this.get_events().addHandler("animationComplete", handler);
  },
  removeOnAnimationComplete: function(handler) {
    this.get_events().removeHandler("animationComplete", handler);
  },
  
  // replay functionality events
  _onReplayStartHandler: function(args) {
    var h = this.get_events().getHandler('replayStart');
    if (h) {
      h(this, args);
    }
  },
  addOnReplayStart: function(handler) {
    this.get_events().addHandler("replayStart", handler);
  },
  removeOnReplayStart: function(handler) {
    this.get_events().removeHandler("replayStart", handler);
  },
  _onReplayStopHandler: function(args) {
    var h = this.get_events().getHandler('replayStop');
    if (h) {
      h(this, args);
    }
  },
  addOnReplayStop: function(handler) {
    this.get_events().addHandler("replayStop", handler);
  },
  removeOnReplayStop: function(handler) {
    this.get_events().removeHandler("replayStop", handler);
  },
  _onReplayPauseHandler: function(args) {
    var h = this.get_events().getHandler('replayPause');
    if (h) {
      h(this, args);
    }
  },
  addOnReplayPause: function(handler) {
    this.get_events().addHandler("replayPause", handler);
  },
  removeOnReplayPause: function(handler) {
    this.get_events().removeHandler("replayPause", handler);
  },
  _onReplayFastForwardHandler: function(args) {
    var h = this.get_events().getHandler('replayFastForward');
    if (h) {
      h(this, args);
    }
  },
  addOnReplayFastForward: function(handler) {
    this.get_events().addHandler("replayFastForward", handler);
  },
  removeOnReplayFastForward: function(handler) {
    this.get_events().removeHandler("replayFastForward", handler);
  },
  _onReplayRewindHandler: function(args) {
    var h = this.get_events().getHandler('replayRewind');
    if (h) {
      h(this, args);
    }
  },
  addOnReplayRewind: function(handler) {
    this.get_events().addHandler("replayRewind", handler);
  },
  removeOnReplayRewind: function(handler) {
    this.get_events().removeHandler("replayRewind", handler);
  },
  
  changePointIcon: function(point, icon) {},
  addPoint: function(point) {
    var allowAdd = false;
    if (this.isAddingPointsEnabled() && this.findPointById(point.getId()) == null) {
      point.setMap(this);
//      if (this.isInViewRegion(point.getLatLng())) {
        allowAdd = true;
//      } else {
//        point._visible = false;
//      }
      this._points.setItem(point.getId(), point);
    }
    return allowAdd;
  },
  movePoint: function(point) {
    var args = {"point": point};
    this._onPointMovedDelegate(args);
  },
  removePointById: function(id) {
    var point = this.findPointById(id);
    if(point) this.removePoint(point);
  },
  removePoint: function(point) {
    if (point != null) {
      point.setWrappedPoint(null);
      this._points.removeItem(point.getId());
    }
  },
  removeAllPoints: function() {
    var point, points;
    var index = 0;
    points = this.getPoints();
    while (points.length > 0) {
      point = points.getItem(points.keys[0]);
      this.removePoint(point);
    }
  },
  
  removePointOfInterest: function(id)
  {
    var point = this.findPointById(id);
    if(point) this.removePoint(point);
    
    var route = this.findRouteById(id);
    if(route) this.removeRouteById(id);
    
    point = this.findPointById(id + "_ic");
    if(point) this.removePoint(point);
  },

  findPointById: function(id) {
    return this._points.getItem(id);
  },
  centerPoint: function(id, zoom) {
    var point = this.findPointById(id);
    if (point != null && !this.isInViewRegion(point.getLatLng())) {
      this.setMapCenter(point.getLatLng(), zoom);
    }
    
    return point;
  },
  getPoints: function() {
    return this._points;
  },
  hidePoint: function(point) {},
  showPoint: function(point) {},
  togglePointsInRegion: function(points, region, hideOthers) {
    hideOthers = typeof(hideOthers) === "undefined" || hideOthers === null ? true : false;
    if (points != null && region != null) {
      var point;
      for (var i=0; i<points.length; i++) {
        point = points.getItem(points.keys[i]);
        if (this.isInRegion(region.topLeft, region.bottomRight, point.getLatLng())) {
          point.show();
        } else {
          if (this.getBubblePoint() != null && point.getId() === this.getBubblePoint().getId()) {
            this.centerDoer(point.getId());
          } else if (hideOthers === true) {
            point.hide();
          }
        }
      }
    }
  },
  enableBubble: function() {
    this._enableBubble = true;
  },
  disableBubble: function() {
    this._enableBubble = false;
  },
  isBubbleEnabled: function() {
    return this._enableBubble;
  },
  showBubble: function(point) {
    this._setBubblePoint(point);
  },
  hideBubble: function(point) {
    this._setBubblePoint(null);
  },
  getBubblePoint: function() {
    return this._bubbledPoint;
  },
  _setBubblePoint: function(point) {
    this._bubbledPoint = point;
  },
  _onPointAddHandler: function(args) {
    var h = this.get_events().getHandler('onPointAdd');
    if (h) {
      h(this, args);
    }
  },
  addOnPointAdd: function(handler) {
    this.get_events().addHandler("onPointAdd", handler);
  },
  removeOnPointAdd: function(handler) {
    this.get_events().removeHandler("onPointAdd", handler);
  },
  _onPointMovedHandler: function(args) {
    var h = this.get_events().getHandler('onPointMoved');
    if (h) {
      h(this, args);
    }
  },
  addOnPointMoved: function(handler) {
    this.get_events().addHandler("onPointMoved", handler);
  },
  removeOnPointMoved: function(handler) {
    this.get_events().removeHandler("onPointMoved", handler);
  },
  _onPointClickHandler: function(args) {
    var h = this.get_events().getHandler('pointClick');
    if (h) {
      h(this, args);
    }
  },
  addOnPointClick: function(handler) {
    this.get_events().addHandler("pointClick", handler);
  },
  removeOnPointClick: function(handler) {
    this.get_events().removeHandler("pointClick", handler);
  },
  _onMapDataboundHandler: function(args) {
    var h = this.get_events().getHandler('mapDatabound');
    if (h) {
      h(this, args);
    }
  },
  addOnMapDatabound: function(handler) {
    this.get_events().addHandler("mapDatabound", handler);
  },
  removeOnMapDatabound: function(handler) {
    this.get_events().removeHandler("mapDatabound", handler);
  },
  _onMapClickHandler: function(args) {
    var h = this.get_events().getHandler('mapClick');
    if (h) {
      h(this, args);
    }
  },
  addOnMapClick: function(handler) {
    this.get_events().addHandler("mapClick", handler);
  },
  removeOnMapClick: function(handler) {
    this.get_events().removeHandler("mapClick", handler);
  },
  _prePointBubbleShowHandler: function(args) {
    var h = this.get_events().getHandler('prePointBubbleShow');
    if (h) {
      h(this, args);
    }
  },
  addPrePointBubbleShow: function(handler) {
    this.get_events().addHandler("prePointBubbleShow", handler);
  },
  removePrePointBubbleShow: function(handler) {
    this.get_events().removeHandler("prePointBubbleShow", handler);
  },
  _onPointBubbleShowHandler: function(args) {
    var h = this.get_events().getHandler('onPointBubbleShow');
    if (h) {
      h(this, args);
    }
  },
  addOnPointBubbleShow: function(handler) {
    this.get_events().addHandler("onPointBubbleShow", handler);
  },
  removeOnPointBubbleShow: function(handler) {
    this.get_events().removeHandler("onPointBubbleShow", handler);
  },
  _prePointBubbleCloseHandler: function(args) {
    var h = this.get_events().getHandler('prePointBubbleClose');
    if (h) {
      h(this, args);
    }
  },
  addPrePointBubbleClose: function(handler) {
    this.get_events().addHandler("prePointBubbleClose", handler);
  },
  removePrePointBubbleClose: function(handler) {
    this.get_events().removeHandler("prePointBubbleClose", handler);
  },
  
  addPhoto: function(photo) {
    var allowAdd = false;
    if (this.findPhotoById(photo.getId()) == null) {
      photo.setMap(this);
      if (this.isInViewRegion(photo.getLatLng())) {
        allowAdd = true;
      } else {
        photo._visible = false;
      }
      this._photos.setItem(photo.getId(), photo);
    }
    return allowAdd;
  },
  removePhotoById: function(id) {
    var photo = this.findPhotoById(id);
    this.removePhoto(photo);
  },
  removePhoto: function(photo) {
    if (photo != null) {
      photo.setWrappedPoint(null);
      this._photos.removeItem(photo.getId());
    }
  },
  removeAllPhotos: function() {
    var photos, photo;
    var index = 0;
    photos = this.getPoints();
    while (photos.length > 0) {
      photo = photos.getItem(photos.keys[0]);
      this.removePoint(photo);
    }
  },
  findPhotoById: function(id) {
    this._photos.getItem(id);
  },
  centerPhoto: function(id, zoom) {
    var photo = this.findPhotoById(id);
    if (photo != null && !this.isInViewRegion(photo.getLatLng())) {
      this.setMapCenter(photo.getLatLng(), zoom);
    }
    
    return photo;
  },
  hidePhoto: function(photo) {},
  showPhoto: function(photo) {},
  showPhotoBubble: function(photo) {
    this._bubbledPoint = photo;
  },
  getPhotos: function() {
    return this._photos;
  },
  
  addDoer: function(doer) {
    var allowAdd = false;
    if (this.findDoerById(doer.getId()) == null) {
      var args = {
        doer: doer
      };
      this._onDoerAddDelegate(args);
      
      doer.setMap(this);
      if (doer.getLatLng() == null){
        allowAdd = false;
      } else if(this.isInViewRegion(doer.getLatLng())) {
        allowAdd = true;
      } else {
        allowAdd = true;
        doer._visible = false;
      }
      this._doers.setItem(doer.getId(), doer);
    }
    return allowAdd;
  },
  removeDoerById: function(id) {
    var doer = this.findDoerById(id);
    this.removeDoer(doer);
  },
  removeDoer: function(doer) {
    if (doer != null) {
      doer.setWrappedPoint(null);
      this._doers.removeItem(doer.getId());
    }
  },
  removeAllDoers: function() {
    var doer, doers;
    var index = 0;
    doers = this.getDoers();
    while (doers.length > 0) {
      doer = doers.getItem(doers.keys[0]);
      this.removeDoer(doer);
    }
  },
  findDoerById: function(id) {
    return this._doers.getItem(id);
  },
  centerDoer: function(id, zoom) {
    var doer = this.findDoerById(id);
    if (doer != null && !this.isInViewRegion(doer.getLatLng())) {
      this.setMapCenter(doer.getLatLng(), zoom);
    }
    
    return doer;
  },
  showDoer: function(doer) {},
  showDoerBubble: function(doer) {
    this._bubbledPoint = doer;
  },
  getDoers: function() {
    return this._doers;
  },
  _onDoerAddHandler: function(args) {
    var h = this.get_events().getHandler('onDoerAdd');
    if (h) {
      h(this, args);
    }
  },
  addOnDoerAdd: function(handler) {
    this.get_events().addHandler("onDoerAdd", handler);
  },
  removeOnDoerAdd: function(handler) {
    this.get_events().removeHandler("onDoerAdd", handler);
  },
  _preDoerBubbleShowHandler: function(args) {
    var h = this.get_events().getHandler('preDoerBubbleShow');
    if (h) {
      h(this, args);
    }
  },
  addPreDoerBubbleShow: function(handler) {
    this.get_events().addHandler("preDoerBubbleShow", handler);
  },
  removePreDoerBubbleShow: function(handler) {
    this.get_events().removeHandler("preDoerBubbleShow", handler);
  },
  
  addPanelControl: function(panel) {
    if (this.findPanelControlById(panel.getId()) == null) {
      Array.add(this._panelControls, panel);
      return true;
    } else {
      return false;
    }
  },
  removePanelControl: function(id) {
    var panel = this.findPanelControlById(id);
    if (panel != null) {
      Array.remove(this._panelControls, panel);
    }
  },
  findPanelControlById: function(id) {
    return this.findInArrayById(id, this._panelControls);
  },
  getPanelControls: function() {
    return this._panelControls;
  },
  showDashboardPanel: function(opts) {},
  hideDashboardPanel: function() {},
  showMapTypePanel: function() {},
  hideMapTypePanel: function() {},
  showReplayPanel: function(options) {
    /// options of which buttons to display
    if (this._onReplayStartDelegate === null) {
      this._onReplayStartDelegate = Function.createDelegate(this, this._onReplayStartHandler);
    }
    
    if (this._onReplayStopDelegate === null) {
      this._onReplayStopDelegate = Function.createDelegate(this, this._onReplayStopHandler);
    }
    
    if (this._onReplayPauseDelegate === null) {
      this._onReplayPauseDelegate = Function.createDelegate(this, this._onReplayPauseHandler);
    }
    
    if (this._onReplayFastForwardDelegate === null) {
      this._onReplayFastForwardDelegate = Function.createDelegate(this, this._onReplayFastForwardHandler);
    }
    
    if (this._onReplayRewindDelegate === null) {
      this._onReplayRewindDelegate = Function.createDelegate(this, this._onReplayRewindHandler);
    }
  },
  hideReplayPanel: function() {},
  showOverviewMapPanel: function() {},
  hideOverviewMapPanel: function() {},
  showLoadingPanel: function() {},
  hideLoadingPanel: function() {},

  isInRegion: function(topleft, bottomright, latlng) {
    if (latlng.getLatitude() < topleft.getLatitude() && latlng.getLatitude() > bottomright.getLatitude() &&
        latlng.getLongitude() > topleft.getLongitude() && latlng.getLongitude() < bottomright.getLongitude()) {
      return true;
    } else {
      return false;
    }
  },
  isInViewRegion: function(latlng) {
    if(latlng == null) return false;
    var v = this.getViewRegion();
    return this.isInRegion(v.topLeft, v.bottomRight, latlng);
  },
  
  findLocation: function(address, callback) {},
  
  dispose: function() {
    this._id = null;
    this._provider = null;
    this._panelControls = null;
    this._points = null;
    this._doers = null;
    this._photos = null;
    this._routes = null;
    this._polylines = null;
    this._originalCenter = null;
    this._map = null;
    this._enableAddingPoints = null;
    this._animatedPoints = [];
    this._animateTimer = null;
    this._animationTickDelegate = null;
    this._bubbledPoint = null;
    this._enableBubble = null;
    this._animationActorCompleteDelegate = null;
    this._animationCompleteDelegate = null;
    this._onReplayStartDelegate = null;
    this._onReplayStopDelegate = null;
    this._onReplayPauseDelegate = null;
    this._onReplayFastForwardDelegate = null;
    this._onReplayRewindDelegate = null;
    
    TrackMe.Mapping.SMap.callBaseMethod(this, 'dispose');
  },

  getType: function() {
    return TrackMe.Mapping.SMap.getName();
  }
};
TrackMe.Mapping.SMap.inheritsFrom(Sys.Component);
TrackMe.Mapping.SMap.registerClass('TrackMe.Mapping.SMap', Sys.Component, TrackMe.IComponent);




// Notify ScriptManager that this is the end of the script.
if (typeof(Sys) !== 'undefined') {
  Sys.Application.notifyScriptLoaded();
}
