// --[ Global Variables ]--------------------------------------------------------------------------
var lastLonLat                = null;
var globalSelectControl       = null;
var latestPositionsBoundary   = null;
var minX                      = null;
var minY                      = null;
var maxX                      = null;
var maxY                      = null;
var globalPositions           = null;

// --[ NetstarAjax Class ]-------------------------------------------------------------------------
function NetstarAjax( aURL, synchronous, anOkHandler, anErrorHandler )
{
  var obj = new Object();

  // ATTRIBUTES
  // ----------------------------------------------------------------------------------------------
  
  obj.xml             = new zXmlHttp.createRequest();
  obj.url             = aURL;
  obj.body            = null;
  obj.type            = null;
  obj.isSynchronous   = synchronous;
  obj.errorFlag       = false;
  obj.okHandler       = null;
  obj.errorHandler    = null;
  
  // METHODS
  // ----------------------------------------------------------------------------------------------
  
  obj.okHandler = function()
  {
    obj.errorFlag = false;
  }

  obj.errorHandler = function()
  {
    obj.errorFlag = true;
  }
  
  obj.inError = function()
  {
    return ( obj.errorFlag );
  }
  
  obj.noError = function()
  {
    return ( ! obj.errorFlag );
  }

  obj.send = function()
  {
    // Pre-conditions:
    if( obj.xml == null ) return;
    
    var result  = false;
    
    obj.xml.open( obj.type, obj.url, obj.isSynchronous );
    obj.xml.setRequestHeader( "Content-Type", "application/x-www-form-urlencoded" );
    obj.xml.send( obj.body );
    
    if( !obj.isSynchronous )
    {
      if( obj.xml.status == 200 ) 
      {
        obj.errorFlag = false;
      } // end if
      else
      {
        obj.errorFlag = true;
      } // end else
    } // end if
    else
    {
    } // end else
    
    return result;
  }
  
  if( anOkHandler != null )
  {
    obj.okHandler = anOkHandler; 
  } // end if
  
  if( anErrorHandler != null )
  {
    obj.errorHandler = anErrorHandler;
  } // end if

  return obj;
}

// ================================================================================================

// --[ NetstarAjaxPost Class ]---------------------------------------------------------------------
function NetstarAjaxPost( aURL, synchronous, anOkHandler, anErrorHandler )
{
  var obj   = NetstarAjax( aURL, synchronous, anOkHandler, anErrorHandler );
  obj.type  = "post";
  
  return obj;
}

// ================================================================================================

// --[ NetstarAjaxGet Class ]----------------------------------------------------------------------
function NetstarAjaxGet( aURL, synchronous, anOkHandler, anErrorHandler )
{
  var obj   = NetstarAjax( aURL, synchronous, anOkHandler, anErrorHandler );
  obj.type  = "get";
  
  return obj;
}

// ================================================================================================

// --[ NetstarControl Class ]----------------------------------------------------------------------
function NetstarControl( aLayer, aMap )
{
  var obj = new Object();


  // ATTRIBUTES
  // ----------------------------------------------------------------------------------------------
  obj.map               = aMap;
  obj.controlLayer      = aLayer;
  obj.selectedFeature   = null;
  obj.popup             = null;
  obj.cloudContent      = null;
  obj.onSelect          = null;
  obj.onUnselect        = null;
  obj.control           = new OpenLayers.Control.SelectFeature
  (
    obj.controlLayer,
    { 
      onSelect:   obj.onSelect,
      onUnselect: obj.onUnselect
    }
  );

  // METHODS
  // ----------------------------------------------------------------------------------------------
  obj.activate = function()
  {
    // Pre-conditions:
    if( obj.control == null ) return null;
    
    var result = obj.control.activate();
    return result;
  }

  obj.deactivate = function()
  {
    // Pre-conditions:
    if( obj.control == null ) return null;
    
    var result = obj.control.deactivate();
    return result;
  }

  obj.addControl = function()
  {
    obj.map.addControl( obj.control );
  }
  
  return obj;
}

// ================================================================================================

// --[ NetstarSelectControl Class ]----------------------------------------------------------------
function NetstarSelectControl( aLayer, aMap )
{
  var obj = NetstarControl( aLayer, aMap );

  // METHODS
  // ----------------------------------------------------------------------------------------------

  obj.onSelect = function( feature )
  {
    // Pre-conditions:
    if( obj.map == null ) return;
    
    obj.selectedFeature = feature;

    // since KML is user-generated, do naive protection against
    // Javascript.
    obj.cloudContent = '<div id="MarkerInfo"><div id="MarkerName">' + feature.attributes.name + '</div><div id="MarkerDescription">' + feature.attributes.description + '</div></div>';
    if( obj.cloudContent.search("<script") != -1 ) 
    {
      obj.cloudContent = "Content contained Javascript! Escaped content below.<br />" + content.replace(/</g, "&lt;");
    } // end if

    obj.popup = new OpenLayers.Popup.FramedCloud
    ( 
      "cloud", 
      feature.geometry.getBounds().getCenterLonLat(),
      new OpenLayers.Size( 100, 100 ),
      obj.cloudContent,
      null, 
      true, 
      obj.onPopupClose
    );
    feature.popup = obj.popup;
    obj.map.addPopup( obj.popup );
  }

  obj.onUnselect = function( feature )
  {
    // Pre-conditions:
    if( feature.popup == null ) return;
    if( obj.map == null ) return;
    
    // remove the popup
    obj.map.removePopup( feature.popup );
    feature.popup.destroy();
    feature.popup = null;
  }

  obj.onPopupClose = function( event )
  {
    // Pre-conditions:
    if( obj.selectedFeature == null ) return;
    if( obj.control == null ) return;
    
    obj.control.unselect( obj.selectedFeature );
  }

  // ATTRIBUTES
  // ----------------------------------------------------------------------------------------------
  obj.control = new OpenLayers.Control.SelectFeature
  (
    obj.controlLayer,
    { 
      onSelect:   obj.onSelect,
      onUnselect: obj.onUnselect
    }
  );

  obj.addControl();
  
  return obj;
}

// ================================================================================================

// --[ NetstarEditControl Class ]------------------------------------------------------------------
function NetstarEditControl( aLayer, aMap )
{
  var obj = NetstarControl( aLayer, aMap );
  
  // ATTRIBUTES
  // ----------------------------------------------------------------------------------------------
  obj.control   = new OpenLayers.Control.ModifyFeature( obj.controlLayer );

  obj.addControl();
  
  return obj;
}

// ================================================================================================

// --[ NetstarPolygonControl Class ]---------------------------------------------------------------
function NetstarPolygonControl( aLayer, aMap )
{
  var obj = new NetstarControl( aLayer, aMap );

  // ATTRIBUTES
  // ----------------------------------------------------------------------------------------------
  obj.control = new OpenLayers.Control.DrawFeature( obj.controlLayer, OpenLayers.Handler.Polygon );
  
  obj.addControl();
  
  return obj;
}
// ================================================================================================

// --[ NetstarRegularPolygonControl Class ]--------------------------------------------------------
function NetstarRegularPolygonControl( aLayer, aMap )
{
  var obj = new NetstarControl( aLayer, aMap );

  // ATTRIBUTES
  // ----------------------------------------------------------------------------------------------
  obj.options = 
  {
    sides: 4,
    snapAngle: 0,
    irregular: true
  }
  
  obj.control = new OpenLayers.Control.DrawFeature
  ( 
    obj.controlLayer, 
    OpenLayers.Handler.RegularPolygon,
    {
      handlerOptions: obj.options
    }
  );
  
  obj.setNumSides = function( nSides )
  {
    obj.options.sides = nSides;
    obj.control.handler.setOptions( {sides: nSides} );
  }
  
  obj.setSnapAngle = function( angle )
  {
    obj.options.snapAngle = angle;
    obj.control.handler.setOptions( {snapAngle: angle} );
  }
  
  obj.setShape = function( isIrregular )
  {
    obj.options.irregular = isIrregular;
    obj.control.handler.setOptions( {irregular: isIrregular} );
  }
  
  obj.addControl();
  
  return obj;
}
// ================================================================================================

// --[ NetstarLayer Class ]------------------------------------------------------------------------

function NetstarLayer( aName, anID, aURL, aMap )
{
  var obj = new Object();

  // ATTRIBUTES
  // ----------------------------------------------------------------------------------------------
  
  obj.id              = anID;
  obj.name            = aName;
  obj.url             = aURL;
  obj.map             = aMap;
  obj.layer           = null;
  obj.selectControl   = null;

  // METHODS
  // ----------------------------------------------------------------------------------------------

  obj.addLayer = function()
  {
    obj.map.addLayer( obj.layer );
  }

  obj.panTo = function( position )
  {
    obj.map.panTo( position );
  }
  
  return obj;
}

// ================================================================================================

// --[ NetstarLabelLayer Class ]-------------------------------------------------------------------
function NetstarLabelLayer( aName, anID, aURL, aMap )
{
  var obj = NetstarLayer( aName, anID, aURL, aMap );
  
  // ATTRIBUTES
  obj.layer             = new OpenLayers.Layer.Markers( aName );
  obj.layer.labelOffset = "15px";
  
  // METHODS
  obj.addPoint = function( x, y, label )
  {
    if( (x != null) && (y != null) && (label != null) )
    {
      obj.layer.addMarker( new OpenLayers.Marker.Label(new OpenLayers.LonLat(x,y), null, label) );
    }
  }
  
  // add the layer to the map
  obj.addLayer();
  
  return obj;
}
// ================================================================================================

// --[ NetstarZoneLayer Class ]--------------------------------------------------------------------
function NetstarZoneLayer( aName, anID, aURL, aMap )
{
  var obj = NetstarLayer( aName, anID, aURL, aMap );

  // ATTRIBUTES
  // ----------------------------------------------------------------------------------------------
  obj.layer = new OpenLayers.Layer.Vector
  ( 
    obj.name
  );

  // CUSTOM CALLBACK HANDLERS
  // ----------------------------------------------------------------------------------------------
  obj.onAddedCallback     = null;
  obj.onModifiedCallback  = null;

  // METHODS
  // ----------------------------------------------------------------------------------------------
  obj.onAdded = function( event )
  {
    if( obj.onAddedCallback != null )
    {
      obj.onAddedCallback( event );
    } // end if
  }

  obj.onModified = function( event )
  {
    if( obj.onAddedCallback != null )
    {
      obj.onAddedCallback( event );
    } // end if
  }

  // INITIALISATION
  // ----------------------------------------------------------------------------------------------
  obj.addLayer();
  
  obj.selectControl  = NetstarSelectControl( obj.layer, obj.map );
  obj.editControl    = NetstarEditControl( obj.layer, obj.map );
  obj.polygonControl = NetstarRegularPolygonControl( obj.layer, obj.map );
  
  obj.layer.events.register( "featureadded", obj.layer.events, obj.onAdded );
  obj.layer.events.register( "afterfeaturemodified", obj.layer.events, obj.onModified );
  
  return obj;
}
// ================================================================================================

// --[ NetstarPositionInfoLayer Class ]------------------------------------------------------------
function NetstarPositionInfo( anID, aName, aGeometry )
{
  var obj = new Object();
  
  // ATTRIBUTES
  // ----------------------------------------------------------------------------------------------
  obj.id        = anID;
  obj.name      = aName;
  obj.geometry  = aGeometry;
  
  return obj;
}
// ================================================================================================

function NetstarUnitPositions()
{
  var obj = new Object();
  
  // ATTRIBUTES
  // ----------------------------------------------------------------------------------------------
  obj.positions = new Array();
  
  // METHODS
  // ----------------------------------------------------------------------------------------------
  obj.append = function( aNetstarPositionInfoObj )
  {
    obj.positions[ obj.positions.length ] = aNetstarPositionInfoObj;
  }
  
  obj.clear = function()
  {
    obj.positions = new Array();
  }
  
  obj.getGeometry = function( aName, id )
  {
    var foundName = false;
    var foundID   = false;
    var found     = false;
    var result    = null;
    
    // find aName in the list of positions
    for( var i = 0; (i < obj.positions.length) && (! found); i += 1 )
    {
      // check against name
      if( aName != null )
      {
        foundName = ( obj.positions[ i ].name.indexOf(aName, 0) >= 0 );
        // check against id
        if( id != null )
        {
          foundID = ( obj.positions[ i ].id == id );
          found   = foundID;
        } // end if
        else
        {
          found = foundName;
        } // end eelse
        
      } // end if
      else if( id != null )
      {
        foundID = ( obj.positions[ i ].id == id );
        found   = foundID;
      } // end if
      
      // get the geometry if the name or id has been found
      if( found )
      {
        result = obj.positions[ i ].geometry;
      } // end if
      
    } // end for
    
    return result;
  }
    
  return obj;
}

// --[ NetstarLatestPositionLayer Class ]----------------------------------------------------------
function NetstarLatestPositionLayer( aName, anID, aURL, aMap )
{
  var obj = NetstarLayer( aName, anID, aURL, aMap );
  
  // ATTRIBUTES
  // ----------------------------------------------------------------------------------------------
  obj.labels = NetstarLabelLayer( aName + " Labels", aName + "Labels", "", obj.map );
  

  // METHODS
  // ----------------------------------------------------------------------------------------------
  obj.zoom = function()
  {
    if( latestPositionsBoundary != null )
    {
      var zoomLevel   = 6;
      /*
      if( mapType == 1 ) 
      {
        // google map - spherical mercator projection
        zoomLevel = obj.layer.getZoomForExtent( latestPositionsBoundary, true );
      }
      else
      {
        // netstar map - normal projection
        zoomLevel = obj.layer.getZoomForExtent( latestPositionsBoundary, true );
      } // end else
      */
      obj.map.zoomTo( zoomLevel );
    } 
  }
  
  
  obj.onAdded = function( event )
  {
    if( event.features.length > 0 )
    {
      var extent = event.features[ event.features.length - 1 ].geometry.getBounds();
      var lonlat = extent.getCenterLonLat();
      
      // create the boundary object if it hasn't been created yet
      if( latestPositionsBoundary == null )
      {
        latestPositionsBoundary = new OpenLayers.Bounds();
      } // end if
      
      // add all lat an lons from event features to the boundary
      
      var features = event.features;
      if( features != null )
      {
        // iterate through the features 
        for( var i = 0; i < features.length; i += 1 )
        {
          var feature = features[ i ];
          if( feature != null )
          {
            var geometry = feature.geometry;
            if( geometry != null )
            {
              if( mapType == 1 )
              {
                // google map
                latestPositionsBoundary.extend( geometry );
                
                // add the label for this feature
                obj.labels.addPoint( geometry.x, geometry.y, feature.attributes.label );
                
                // add to global position object
                if( globalPositions != null )
                {
                  globalPositions.append( NetstarPositionInfo( i, feature.attributes.name, geometry ) );
                } // end if
              } 
              else
              {
                // netstar map
                latestPositionsBoundary.extend( geometry );
                lonlat = latestPositionsBoundary.getCenterLonLat();
                
                // add the label for this feature
                obj.labels.addPoint( geometry.x, geometry.y, feature.attributes.label );
                if( globalPositions != null )
                {
                  globalPositions.append( NetstarPositionInfo( i, feature.attributes.name, geometry ) );
                } // end if
              } // end else
            }
          }
          
        } // end for
      } // end if
      
      lastLonLat = lonlat;
      
      obj.panTo( lonlat );
      obj.zoom();
    } // end if
  }

  // ATTRIBUTES
  // ----------------------------------------------------------------------------------------------
  obj.layer = new OpenLayers.Layer.GML
  (
    obj.name,
    obj.url,
    {
      format: OpenLayers.Format.KML,
      formatOptions: 
      {
        extractStyles:      true,
        extractAttributes:  true,
        styleMap:
          new OpenLayers.StyleMap
          (
            {
              'default':
              {
                strokeColor:    "#00FF00",
                strokeOpacity:  1,
                strokeWidth:    3,
                fillColor:      "#FF5500",
                fillOpacity:    0.5,
                pointRadius:    6,
                pointerEvents:  "visiblePainted",
                label :         "Test",
                fontSize:       "12px",
                fontFamily:     "Arial",
                fontWeight:     "bold"
              }
            }
          )
      },
      eventListeners: 
      { 
        "featuresadded": obj.onAdded
      }
    }
  );

  // INITIALISATION
  // ----------------------------------------------------------------------------------------------
  obj.addLayer();
  obj.selectControl = NetstarSelectControl( obj.layer, obj.map );
  obj.selectControl.activate();
  
  return obj;
}
// ================================================================================================

// --[ NetstarHistoryPositionsLayer Class ]--------------------------------------------------------
function NetstarHistoryPositionsLayer( aName, anID, aURL, aMap )
{
  var obj = NetstarLayer( aName, anID, aURL, aMap );

  // ATTRIBUTES
  // ----------------------------------------------------------------------------------------------
  obj.labels = NetstarLabelLayer( aName + " Labels", aName + "Labels", "", obj.map );


  // METHODS
  // ----------------------------------------------------------------------------------------------
  obj.onAdded = function( event )
  {
    var features = event.features;
    
    // ensure the features are valid
    if( features != null )
    {
      var lastFeature = features[ features.length - 1 ];
      
      // ensure the feature is valid
      if( lastFeature == null ) return;
      
      // ensure that the feature geometry is valid
      if( lastFeature.geometry == null ) return;
      
      var extent = lastFeature.geometry.getBounds();
      // ensure that the extent is valid
      if( extent == null ) return;
      
      var lonlat = extent.getCenterLonLat();
      // ensure that the longitude and latitude is valid
      if( lonlat == null ) return;
      
      // iterate through the features 
      for( var i = 0; i < features.length; i += 1 )
      {
        var feature = features[ i ];
        if( feature != null )
        {
          var geometry = feature.geometry;
          if( geometry != null )
          {
            obj.labels.addPoint( geometry.x, geometry.y, feature.attributes.label );
            if( globalPositions != null )
            {
              globalPositions.append( NetstarPositionInfo( i, feature.attributes.name, geometry ) );
            } // end if
          } // end if
        } // end if
        
      } // end for
      
      obj.panTo( lonlat );
    } // end if
  }
  
  // ATTRIBUTES
  // ----------------------------------------------------------------------------------------------
  obj.layer = new OpenLayers.Layer.GML
  (
    obj.name,
    obj.url,
    {
      format: OpenLayers.Format.KML,
      formatOptions: 
      {
        extractStyles:      true,
        extractAttributes:  true
      },
      eventListeners: 
      { 
        "featuresadded": obj.onAdded
      }
    }
  );

  // INITIALISATION
  // ----------------------------------------------------------------------------------------------
  obj.addLayer();
  obj.selectControl = new NetstarSelectControl( obj.layer, obj.map );
  obj.selectControl.activate();
  
  return obj;
}

// ================================================================================================

// --[ NetstarRouteLayer Class ]-------------------------------------------------------------------
function NetstarRouteLayer( aName, anID, aURL, aMap )
{
  var obj = NetstarLayer( aName, anID, aURL, aMap );
  
  // ATTRIBUTES
  // ----------------------------------------------------------------------------------------------
  obj.layer = new OpenLayers.Layer.GML
  ( 
    obj.name, 
    obj.url, 
    { 
      format: OpenLayers.Format.KML, 
      formatOptions: 
        { 
          extractStyles: true, 
          extractAttributes: true 
        } 
    } 
  );

  // METHODS
  // ----------------------------------------------------------------------------------------------

  // INITIALISATION
  // ----------------------------------------------------------------------------------------------
  obj.addLayer();
  
  return obj;
}

// ================================================================================================

// --[ NetstarMapLayer Class ]---------------------------------------------------------------------

function NetstarMapLayer( aMap, anID, aName, aHeight, aWidth, nZoomLevels, aZoomLevel )
{
  var obj = new Object;
  
  // ATTRIBUTES
  // ----------------------------------------------------------------------------------------------
  obj.map         = aMap;
  obj.id          = anID;
  obj.name        = aName;
  obj.width       = aWidth;
  obj.height      = aHeight;
  obj.zoomLevels  = nZoomLevels;
  obj.zoomLevel   = aZoomLevel;
  obj.layer       = null;
  
  // METHODS
  // ----------------------------------------------------------------------------------------------
  obj.addLayer = function()
  {
    obj.map.addLayer( obj.layer );
  };
  
  // INITIALISATION
  // ----------------------------------------------------------------------------------------------
  
  return obj;
}

// ================================================================================================

// --[ NetstarDefaultMapLayer Class ]--------------------------------------------------------------
function NetstarDefaultMapLayer( aMap, anID, aName, aHeight, aWidth, nZoomLevels, aZoomLevel )
{
  var obj = NetstarMapLayer( aMap, anID, aName, aHeight, aWidth, nZoomLevels, aZoomLevel );
  
  // ATTRIBUTES
  // ----------------------------------------------------------------------------------------------
  var urls = 
  [
    "http://test.netstaronline.net:8000/images",
    "http://test.netstaronline.net:8001/images",
    "http://test.netstaronline.net:8002/images"
  ];
  
//  var urls = 
//  [
//    "http://test.netstaronline.net:9000/images",
//    "http://test.netstaronline.net:9001/images",
//    "http://test.netstaronline.net:9002/images"
//  ];
//  
  obj.layer = new OpenLayers.Layer.WMS
  (
    obj.name,
    urls,//"http://test.netstaronline.net:9000/images",
    { 
      request:            "openlayers", 
      layers:             "",
      styles:             "", 
      crs:                "EPSG:4326",
      format:             "image/png", 
      version:            "1.3.0",
      transparent:        "True",
      opacity:            0.3,
      attribution:        "Provided by Altech Netstar",
      usertoken:          "uuid-916c52f6-7d7d-4722-b7a5-0bd52d15f8b3-1"
    },
    { 
      'buffer':           0,
      'numZoomLevels':    obj.zoomLevels,
      'isBaseLayer':      true,
      'visibility':       true,
      'transitionEffect': "resize"
    }, 
    { 
      'reproject':        true
    }
  );
  
  // METHODS
  // ----------------------------------------------------------------------------------------------
  
  // INITIALISATION
  // ----------------------------------------------------------------------------------------------
  obj.addLayer();
  obj.map.zoomTo( obj.zoomLevel );

  return obj;
}

// ================================================================================================

// --[ NetstarGoogleStreetsLayer Class ]-----------------------------------------------------------
function NetstarGoogleStreetsLayer( aMap, anID, aName, aHeight, aWidth, nZoomLevels, aZoomLevel )
{
  var obj = NetstarMapLayer( aMap, anID, aName, aHeight, aWidth, nZoomLevels, aZoomLevel );
  
  // ATTRIBUTES
  // ----------------------------------------------------------------------------------------------
  obj.layer      = new OpenLayers.Layer.Google
  (
    obj.name,
    {
      'isBaseLayer':        true,
      'transitionEffect':   "resize",
      'sphericalMercator':  true
    }
  );

  
  // METHODS
  // ----------------------------------------------------------------------------------------------
  
  // INITIALISATION
  // ----------------------------------------------------------------------------------------------
  obj.addLayer();
  //obj.map.zoomToMaxExtent();
  
  return obj;
}

// ================================================================================================

// --[ NetstarGoogleHybridLayer Class ]------------------------------------------------------------

function NetstarGoogleHybridLayer( aMap, anID, aName, aHeight, aWidth, nZoomLevels, aZoomLevel )
{
  var obj = NetstarMapLayer( aMap, anID, aName, aHeight, aWidth, nZoomLevels, aZoomLevel );
  
  // ATTRIBUTES
  // ----------------------------------------------------------------------------------------------
  obj.layer      = new OpenLayers.Layer.Google
  (
    obj.name,
    {
      'isBaseLayer':        true,
      'transitionEffect':   "resize",
      'sphericalMercator':  true,
      type:                 G_HYBRID_MAP
    }
  );
  
  // METHODS
  // ----------------------------------------------------------------------------------------------
  
  // INITIALISATION
  // ----------------------------------------------------------------------------------------------
  obj.addLayer();
  //obj.map.zoomToMaxExtent();
  
  return obj;
}

// ================================================================================================

// --[ NetstarMapControl Class ]-------------------------------------------------------------------



function NetstarMapControl( aPlaceholder, aWidth, aHeight, defaultZoomLevel )
{
  var obj = new Object();
  
  // ATTRIBUTES
  // ----------------------------------------------------------------------------------------------
  obj.layers        = new Array();
  obj.placeholder   = aPlaceholder;
  obj.width         = aWidth;
  obj.height        = aHeight;
  obj.zoomLevel     = defaultZoomLevel;
  obj.map           = null;
  
  // METHODS
  // ----------------------------------------------------------------------------------------------
  
  obj.addGoogleStreets = function()
  {
    if( obj.map == null )
    {
      var options = 
      {
        //projection:         new OpenLayers.Projection("EPSG:900913"),
        //displayProjection:  new OpenLayers.Projection("EPSG:4326"),
        units:              "m",
        numZoomLevels:      20,
        maxResolution:      156543.0339,
        maxExtent:          new OpenLayers.Bounds(-20037508, -20037508, 20037508, 20037508.34)
      };
      obj.map = new OpenLayers.Map( obj.placeholder, options );
    }

    obj.map.addControl( new OpenLayers.Control.LayerSwitcher({ 'ascending': true }) );
    obj.map.addControl( new OpenLayers.Control.ScaleLine() );
    obj.map.addControl( new OpenLayers.Control.MousePosition() );
    obj.map.addControl( new OpenLayers.Control.KeyboardDefaults() );

    var o = NetstarGoogleStreetsLayer( obj.map, "Google", "Google Streets", obj.width, obj.height, 20, defaultZoomLevel );
    obj.layers[ obj.layers.length ] = o.layer;
    return o;
  }

  obj.addGoogleHybrid = function()
  {
    if( obj.map == null )
    {
      var options = 
      {
        //projection:         new OpenLayers.Projection("EPSG:900913"),
        //displayProjection:  new OpenLayers.Projection("EPSG:4326"),
        units:              "m",
        numZoomLevels:      20,
        maxResolution:      156543.0339,
        maxExtent:          new OpenLayers.Bounds(-20037508, -20037508, 20037508, 20037508.34)
      };
      obj.map = new OpenLayers.Map( obj.placeholder, options );
    }

    obj.map.addControl( new OpenLayers.Control.LayerSwitcher({ 'ascending': true }) );
    obj.map.addControl( new OpenLayers.Control.ScaleLine() );
    obj.map.addControl( new OpenLayers.Control.MousePosition() );
    obj.map.addControl( new OpenLayers.Control.KeyboardDefaults() );
    
    var o = NetstarGoogleHybridLayer( obj.map, "Google", "Google Hybrid", obj.width, obj.height, 20, defaultZoomLevel );
    obj.layers[ obj.layers.length ] = o.layer;
    return o;
  }

  obj.addNetstarDefault = function()
  {
    if( obj.map == null )
    {
      obj.map = new OpenLayers.Map
      (
        obj.placeholder,
        { 
          controls: 
          [
            new OpenLayers.Control.Navigation(),
            new OpenLayers.Control.PanPanel(),
            new OpenLayers.Control.ZoomPanel(),
            new OpenLayers.Control.LayerSwitcher({ 'ascending': true }),
            new OpenLayers.Control.ScaleLine(),
            new OpenLayers.Control.MousePosition(),
            new OpenLayers.Control.KeyboardDefaults()
          ]
        }
      );
    }
    
    var o = NetstarDefaultMapLayer( obj.map, "Netstar", "Netstar Map", obj.width, obj.height, 20, defaultZoomLevel );
    obj.layers[ obj.layers.length ] = o.layer;
    return o;
  }

  obj.addZoneLayer = function( name, id, url )
  {
    var o = NetstarZoneLayer( name, id, url, obj.map );
    obj.layers[ obj.layers.length ] = o.layer;
    return o;
  }

  obj.addLatestPositionsLayer = function( name, id, url )
  {
    var o = NetstarLatestPositionLayer( name, id, url, obj.map );
    obj.layers[ obj.layers.length ] = o.layer;
    obj.map.zoomTo( obj.zoomLevel );
    return o;
  }

  obj.addHistoryPositionsLayer = function( name, id, url )
  {
    var o = NetstarHistoryPositionsLayer( name, id, url, obj.map );
    obj.layers[ obj.layers.length ] = o.layer;
    obj.map.zoomTo( obj.zoomLevel );
    return o;
  }

  obj.addRouteLayer = function( name, id, url )
  {
    var o = new NetstarRouteLayer( name, id, url, obj.map );
    obj.layers[ obj.layers.length ] = o.layer;
    return o;
  }
  
  obj.addLabelLayer = function( name, id )
  {
    var o = new NetstarRouteLayer( name, id, "", obj.map );
    obj.layers[ obj.layers.length ] = o.layer;
    return o;
  }
  
  // INITIALISATION
  // ----------------------------------------------------------------------------------------------
  
  return obj;
}

