/* DEMO USAGE:

    function refreshLatestPosition()
    {
      if( wms != null )
      {
        wms.latestPositions();
      } // end if
    }
    
    function navigateMap()
    {
      panel.activateNavigation();
    }
    
    function selectZone()
    {
      panel.activateSelectZone();
    }
    
    function addGoZone()
    {
      panel.activateNewGoZone();
    }
    
    function addNoGoZone()
    {
      panel.activateNewNoGoZone();
    }
    
    function saveZone()
    {
      panel.activateSaveZone();
    }



  var wms     = WebMapServerObject( "map", "http://itrack.captor.com.my:8000/images", 790, 400, CONST_NETSTAR_MAP, true, true, true, true, true );
  cookies = extractCookies( cookies );
  
  var numUnits = cookies[ "UnitCount" ];

  // process each unit in the cookie    
  for( index = 0; index < numUnits; index += 1 )
  {
    var unitNumber  = cookies[ "Unit" + index ];
    wms.markerURLs[ index ] = "ServiceProxy.aspx?unit=" + unitNumber;
  } // end for
  
  if( wms != null )
  {
    wms.latestPositions();
    //window.setInterval( refreshLatestPosition, (1000 * refreshTime) );
  } // end if
  
  wms.addZones();
  
  var panel = ZonePanel( wms, true, true, true, true, true );
  
  panel.btnNavigate.imageURL    = "../img/navigate.png";
  panel.btnSelectZone.imageURL  = "../img/select.png";
  panel.btnNewGoZone.imageURL   = "../img/gozone.png";
  panel.btnNewNoGoZone.imageURL = "../img/nogozone.png";
  panel.btnSave.imageURL        = "../img/save.png";
  
  panel.btnNavigate.onClick     = "navigateMap();";
  panel.btnSelectZone.onClick   = "selectZone();";
  panel.btnNewGoZone.onClick    = "addGoZone();";
  panel.btnNewNoGoZone.onClick  = "addNoGoZone();";
  panel.btnSave.onClick         = "saveZone();"
  
  panel.btnNavigate.build();
  panel.btnSelectZone.build();
  panel.btnNewGoZone.build();
  panel.btnNewNoGoZone.build();
  panel.btnSave.build();
  
  panel.draw();

*/


var CONST_NETSTAR_MAP       = "Netstar Map";
var CONST_GOOGLE_STREETS    = "Google Streets";
var CONST_GOOGLE_PHYSICAL   = "Google Physical";
var CONST_GOOGLE_HYBRID     = "Google Hybrid";
var CONST_GOOGLE_SATELLITE  = "Google Satellite";


// [ methods to test url's ]=======================================================================
function sendRequest( action ) 
{
  var oxml    = zXmlHttp.createRequest();
  var result  = false;
  
  oxml.open( "get", action, false ); // synchronous call
  oxml.setRequestHeader( "Content-Type", "application/x-www-form-urlencoded" );
  oxml.send( null );
  
  if( oxml.status == 200 ) 
  {
    result = true;
  } // end if
  else
  {
    result = false;
  } // end else
  
  return result;
}

function httpGet( url )
{
  var oxml    = zXmlHttp.createRequest();
  var result  = "";
  
  oxml.open( "get", url, false ); // synchronous call
  oxml.setRequestHeader( "Content-Type", "application/x-www-form-urlencoded" );
  oxml.send( null );
  
  if( oxml.status == 200 ) 
  {
    result = oxml.responseText;
  } // end if
  
  return result;
}

function testGML( address )
{
  var result = sendRequest( address );
  
  return result;
}

// [ methods to extract cookie details ]===========================================================
var cookies = Array();
function extractCookies( _cookies )
{
  var name, value;
  var beginning, middle, end;

  beginning = 0;  // start at beginning of cookie string

  while (beginning < document.cookie.length)
  {
    middle  = document.cookie.indexOf('=', beginning);  // find next =
    end     = document.cookie.indexOf(';', beginning);  // find next ;

    if (end == -1)  // if no semicolon exists, it's the last cookie
      end = document.cookie.length;
      
    if ( (middle > end) || (middle == -1) )
    { // if the cookie has no value... 
      name = document.cookie.substring(beginning, end);
      value = "";
    }
    else
    { // extract its value
      name = document.cookie.substring(beginning, middle);
      value = document.cookie.substring(middle + 1, end);
    }

    _cookies[ name ] = unescape(value);  // add it to the associative array
    beginning = end + 2;  // step over space to beginning of next cookie
  }

  return _cookies;
}



function MapObject( onZoneAdded, onZoneModified, onMarkerAdded, onSelectMarker, onUnselectMarker )
{
  var map = new Object();
  
  // [ map object properties ]=====================================================================
  
  map.name                = CONST_NETSTAR_MAP;
  map.placeholder         = "map";
  map.url                 = "http://itrack.captor.com.my:8000/images";
  map.request             = "openlayers";
  map.layers              = "";
  map.styles              = "";
  map.crs                 = "EPSG:4326";
  //map.bbox                = "-180,-90,180,90";
  map.width               = "500";
  map.height              = "300";
  map.format              = "image/png";
  map.version             = "1.3.0";
  map.transparent         = "True";
  map.opacity             = 0.3;
  map.attribution         = "";
  map.usertoken           = "uuid-916c52f6-7d7d-4722-b7a5-0bd52d15f8b3-1";
  map.buffer              = 10;
  map.visibility          = true;
  map.transitionEff       = null;
  map.reproject           = true;
  map.isGoogleMap         = false;
  map.googleMapType       = null;
  map.zoomLevels          = 20;
  map.currentZoom         = 14;
  map.selectedLayer       = CONST_NETSTAR_MAP;
  map.selectedFeature     = null;
  map.zonePanel           = null;
  map.zoneCreateLimit     = 1;
  map.zoneCreateCount     = 0;
  map.zoneCreating        = false;
  map.timezoneURL         = "ServiceProxy.aspx";
  map.markerURLs          = new Array();
  map.tripMarkerURLs      = new Array();
  map.routeURLs           = new Array();
  map.zoneURLs            = new Array();
  map.nameMLayers         = new Array();
  map.nameTMLayers        = new Array();
  map.nameRLayers         = new Array();
  map.nameZLayers         = new Array();
  map.tripMarkerLayers    = new Array();
  map.markerLayers        = new Array();
  map.routeLayers         = new Array();
  map.zoneLayers          = new Array();
  map.controls            = new Array();
  map.ctrlMarkerSel       = null;
  map.ctrlZoneSel         = null;
  map.ctrlZonePolygon     = null;
  map.ctrlZoneModify      = null;
  map.inError             = false;
  map.popup               = null;
  map.OLMap               = null; // this property stores the OpenLayers map
  map.zones               = null; // this property will store all the zones defined on the map
  map.doSaveZones         = null; // this function should return true if the zones were indeed saved otherwise false
  
  // initialise the array objects
  map.markerURLs[ 0 ]       = "ServiceProxy.aspx";
  map.tripMarkerURLs[ 0 ]   = "ServiceProxy.aspx";
  map.routeURLs[ 0 ]        = "ServiceProxy.aspx";
  map.zoneURLs[ 0 ]         = "ServiceProxy.aspx";
  
  map.nameMLayers[ 0 ]      = "Marker";
  map.nameTMLayers[ 0 ]     = "Trip Markers";
  map.nameRLayers[ 0 ]      = "Route";
  map.nameZLayers[ 0 ]      = "Zone";
  
  // [ map object methods ]========================================================================
  
  // this method will zoom to a feature at the current zoom level
  map.zoomInToFeature = function()
  { 
    if( map.OLMap != null ) 
    {
      map.OLMap.zoomTo( map.currentZoom );
    }    
  }; // end function zoomToFeature
  
  // [ event handlers ]============================================================================
  
  // this method will save the name of the selected layer
  map.baseLayerChanged = function( event )
  {
    map.selectedLayer = event.layer.name;
    //alert( "base: " + map.selectedLayer );
  }; // end function baseLayerChanged
  
  // this method will save the name of the selected layer
  map.layerChanged = function( event )
  {
    map.selectedLayer = event.layer.name;
    //alert( "layer: " + map.selectedLayer );
  }; // end function layerChanged

  
  // returns a 2D array of points
  map.getZonesFromMap = function( event )  
  {
    var zones = new Array();
    
    if( map.zoneLayers[0] != null )
    {
      // process each drawn zone
      for( var i = 0; i < map.zoneLayers[0].features.length; i += 1 )
      {
        var feature   = map.zoneLayers[0].features[ i ];
        var geometry  = feature.geometry;
        
        if( geometry != null )
        {
          // ensure the geometry components are valid
          if( geometry.components != null )
          {
            // ensure there are components
            if( geometry.components.length > 0 ) 
            {
              var c0 = geometry.components[ 0 ];
              
              // ensure the ring is valid
              if( c0 != null )
              {
                var ring  = c0.components;
                
                // save the zone points
                
                zones[ i ] = ring;
                //alert( "ring added: " + ring );
              }
            } // end if
          } // end if
          
        }
      } // end for
    } // end if
    
    return zones;
  } 
  
  map.onZoneAdded = function( event )  
  {
    map.zoneCount += 1;
    
    if( map.newZoneCount == 1 )
    {
      map.newZoneCount = 2;
    } // end if
    
    if( map.zoneCreating )
    {
      map.zoneCreateCount += 1;
      map.redraw();
      map.deactivateControls();
    } // end if
    
    return map.getZonesFromMap( event );
  }; // end function onZoneAdded
  
  map.onZoneModified = function( event )  
  {
    return map.getZonesFromMap( event );
  }; // end function onZoneModified
  
  
  // this method will pan to a marker when it is added to the map
  map.onMarkerAdded = function( event )
  {
    var index     = event.features.length - 1;
    var feature   = event.features[ index ];
  
    if( feature != null )
    {
      var geometry  = feature.geometry;
      
      if( geometry != null )
      {
        var extent = geometry.getBounds();
        
        if( extent != null )
        {
          var position = extent.getCenterLonLat();
          
          map.OLMap.panTo( position );
        }
      } // end if
    } // end if
  }; // end function onMarkerAdded

  map.onMarkerPopupClose = function( event )
  {
    if( map.selectedFeature != null )
    {
      if( map.ctrlMarkerSel != null )
      {
        map.ctrlMarkerSel.unselect( map.selectedFeature );
      }
    }
  } // end function onTripPopupClose

  map.onSelectMarker = function( feature ) 
  {
    map.selectedFeature = feature;

    // since KML is user-generated, do naive protection against
    // Javascript.

    var content = "<h1>" + feature.attributes.name + "</h1><p>" + feature.attributes.description + "</p>";
    if (content.search("<script") != -1) 
    {
      content = "Content contained Javascript! Escaped content below.<br />" + content.replace(/</g, "&lt;");
    }

    map.popup = new OpenLayers.Popup.FramedCloud
    ( 
      "chicken", 
      feature.geometry.getBounds().getCenterLonLat(),
      new OpenLayers.Size( 100, 100 ),
      content,
      null, 
      true, 
      map.onMarkerPopupClose
    );
    feature.popup = map.popup;
    map.OLMap.addPopup( map.popup );
  }; // end function onSelectMarker

  map.onUnselectMarker = function( feature ) 
  {
    if( feature.popup != null )
    {
      map.OLMap.removePopup( feature.popup );
      feature.popup.destroy();
      feature.popup = null;
    }
  }; // end function onUnselectMarker

////
  map.onZonePopupClose = function( event )
  {
    if( map.selectedFeature != null )
    {
      if( map.ctrlZoneSel != null )
      {
        map.ctrlZoneSel.unselect( map.selectedFeature );
      }
    }
  }

  map.onSelectZone = function( feature ) 
  {
    map.selectedFeature = feature;

    // Since KML is user-generated, do naive protection against
    // Javascript.
    var content = "<h1>" + feature.attributes.name + "</h1><p>" + feature.attributes.description + "</p>";
    if (content.search("<script") != -1) 
    {
      content = "Content contained Javascript! Escaped content below.<br />" + content.replace(/</g, "&lt;");
    }

    map.popup = new OpenLayers.Popup.FramedCloud
    ( 
      "Zone", 
      feature.geometry.getBounds().getCenterLonLat(),
      new OpenLayers.Size( 100, 100 ),
      content,
      null, 
      true, 
      map.onZonePopupClose 
    );
    feature.popup = map.popup;
    map.OLMap.addPopup( map.popup );
  }

  map.onUnselectZone = function( feature ) 
  {
    if( feature.popup != null )
    {
      map.OLMap.removePopup( feature.popup );
      feature.popup.destroy();
      feature.popup = null;
    }
  }
////  
  
  
  
  // this method will remove the marker and route layers from the map if they exist
  map.removeLayers = function()
  {
    var features;
    
    // loop through the marker layers
    for( i = 0; i < map.markerLayers.length; i += 1 )
    {
      // remove any popups
      features = map.markerLayers[ i ].features;
      
      // test if a Feature has a PopUp and then Remove it
      for( feature in features ) 
      {
        if( features[feature].popup != null ) 
        {
          map.OLMap.removePopup( features[feature].popup );
          features[ feature ].popup.destroy();
          features[ feature ].popup = null;
        }
      }

      map.OLMap.removeLayer( map.markerLayers[ i ], false );
    } // end for

    // loop through the trip marker layers
    for( i = 0; i < map.tripMarkerLayers.length; i += 1 )
    {
      // remove any popups
      features = map.tripMarkerLayers[ i ].features;
      
      // test if a Feature has a PopUp and then Remove it
      for( feature in features ) 
      {
        if( features[feature].popup != null ) 
        {
          map.OLMap.removePopup( features[feature].popup );
          features[ feature ].popup.destroy();
          features[ feature ].popup = null;
        }
      }

      map.OLMap.removeLayer( map.markerLayer[ i ], false );
    } // end for

    // loop through the route layers
    for( i = 0; i < map.routeLayers.length; i += 1 ) 
    {
      map.OLMap.removeLayer( map.routeLayer[ i ], false );
    } // end if
  }; // end function removeLayers

  // this method will create a zone layer and add it to the OLMap object
  map.addZones = function()
  {
    var index = map.zoneLayers.length;
    map.zoneLayers[ index ] = new OpenLayers.Layer.GML
    (
      map.nameZLayers[ index ],
      map.zoneURLs[ index ],
      {
        format:               OpenLayers.Format.KML,
        formatOptions:
        {
          extractStyles:      true,
          extractAttributes:  true
        },
        eventListeners:
        {
          onSelect:           map.onSelectZone, 
          onUnselect:         map.onUnselectZone 
        }
      }
    );
    
    map.zoneLayers[ index ].events.on
    (
      {
        "featureadded":         map.onZoneAdded,
        "afterfeaturemodified": map.onZoneModified 
      }
    );
    
    // add the layer to the map if it is valid
    if( map.OLMap != null )
    {
      map.OLMap.addLayer( map.zoneLayers[index] );
    } // end if
    
    // create the zone polygon control
    map.ctrlZonePolygon = new OpenLayers.Control.DrawFeature( map.zoneLayers[index], OpenLayers.Handler.Polygon );

    // create the zone modification control
    map.ctrlZoneModify = new OpenLayers.Control.ModifyFeature( map.zoneLayers[index] );

    // create the zone selection control
    map.ctrlZoneSel = new OpenLayers.Control.SelectFeature
    (
      map.zoneLayers[ index ],
      { 
        onSelect:   map.onSelectZone, 
        onUnselect: map.onUnselectZone 
      }
    );
    

    map.OLMap.addControl( map.ctrlZoneSel );
    map.OLMap.addControl( map.ctrlZonePolygon );
    map.OLMap.addControl( map.ctrlZoneModify );
    
    map.ctrlZoneSel.activate();
  }; // end function addZones
  
  // this method will save the zones from the frist zone layer on the map to the map object zones attribute
  map.saveZones = function()
  {
    map.zones = map.getZonesFromMap( null );
    
    // call the save handler if it has beend defined
    if( map.doSaveZones != null )
    {
      var result = map.doSaveZones();
      
      if( result )
      {
        map.deactivateControls();
        map.zoneCreateCount = 0;
        map.zoneCreating    = false;
      } // end if
    } // end if
  }; // end function saveZones
  
  // determines if the zone creation limit has been reached or exceeded
  map.isLimitReached = function()
  {
    return ( map.zoneCreateCount >= map.zoneCreateLimit );
  }; // end function isLimitReached
  
  // this method will test if the latest position is available for a unit
  map.testLatestPositions = function()
  {
    map.inError = false; 
   
    // loop through the markers
    for( i = 0; i < map.markerURLs.length; i += 1 )
    {
      //alert( map.markerURLs[ i ] );
      map.inError |= !testGML( map.markerURLs[ i ] );
    } // end for
  }; // end function testLatestPosition
  
  map.testTripReplay = function()
  {
    map.inError = false;
   
    // loop through the markers
    for( i = 0; i < map.tripMarkerURLs.length; i += 1 )
    {
      //alert( map.tripMarkerURLs[ i ] );
      map.inError |= !testGML( map.tripMarkerURLs[ i ] );
    } // end for
   
    // loop through the routes
    for( i = 0; i < map.routeURLs.length; i += 1 )
    {
      //alert( map.routeURLs[ i ] );
      map.inError |= !testGML( map.routeURLs[ i ] );
    } // end for
  };
  
  // this method will add the latest position of a unit to the map
  map.latestPosition = function( doCreate )
  {
    // PRECONDITION: ensure there isn't a problem with latest position urls
    map.testLatestPositions();
    if( map.inError )
    {
      alert( "Error retrieving latest positions" );
      return;
    } // end if
  
    map.removeLayers();
    var index = 0;
    
    if( doCreate )
    {
      index = map.markerLayers.length;
    } // end if
    else
    {
      index = map.markerLayers.length - 1;
    } // end else
    
    map.markerLayers[ index ] = new OpenLayers.Layer.GML
    (
      map.nameMLayers[ index ],
      map.markerURLs[ index ],
      {
        format:               OpenLayers.Format.KML,
        formatOptions: 
        {
          extractStyles:      true,
          extractAttributes:  true
        },
        eventListeners: 
        { 
          "featuresadded":    map.onMarkerAdded
        }
      }
    ); 
    
    // add the markers
    map.OLMap.addLayer( map.markerLayers[ index ] );

    map.ctrlMarkerSel = new OpenLayers.Control.SelectFeature
    (
      map.markerLayers[ index ],
      { 
        onSelect:   map.onSelectMarker, 
        onUnselect: map.onUnselectMarker 
      }
    );

    map.OLMap.addControl( map.ctrlMarkerSel );
    map.ctrlMarkerSel.activate();
    
    map.zoomInToFeature();
  }; // end function latestPosition
  
  // this method will add the latest positions of multiple units to the map
  map.latestPositions = function()
  {
    map.removeLayers();
    cookies = extractCookies( cookies );
    var numUnits = cookies[ "UnitCount" ];

    // process each unit in the cookie    
    for( index = 0; index < numUnits; index += 1 )
    {
      var unitNumber  = cookies[ "Unit" + index ];
      var unitName    = "Unit: " + unitNumber;
      //map.markerURLs[ index ] = "ServiceProxy.aspx?unit=" + unitNumber;
      
      // test each unit url before creating a layer for it
      if( !testGML( map.markerURLs[ index ] ) )
      {
        alert( "Error retrieving latest position for " + unitName );
      } // end if
      else
      {
        map.nameMLayers[ index ]  = unitName;
        map.markerLayers[ index ] = new OpenLayers.Layer.GML
        (
          map.nameMLayers[ index ],
          map.markerURLs[ index ],
          {
            format:               OpenLayers.Format.KML,
            formatOptions: 
            {
              extractStyles:      true,
              extractAttributes:  true
            },
            eventListeners: 
            { 
              "featuresadded":    map.onMarkerAdded
            }
          }
        ); 
        
        // add the markers
        map.OLMap.addLayer( map.markerLayers[ index ] );

        map.ctrlMarkerSel = new OpenLayers.Control.SelectFeature
        (
          map.markerLayers[ index ],
          { 
            onSelect:   map.onSelectMarker, 
            onUnselect: map.onUnselectMarker 
          }
        );

        map.OLMap.addControl( map.ctrlMarkerSel );
        map.ctrlMarkerSel.activate();
        
        map.zoomInToFeature();
      } // end else
      
    } // end for
    
  }; // end function latestPositions
  
  map.tripReplay = function( doCreate ) 
  {
    // PRECONDITION: ensure there isn't a problem with trip position urls
    map.testTripReplay();
    if( map.inError )
    {
      alert( "Error retrieving trip replay" );
      return;
    } // end if
  
    map.removeLayers();
    var index = 0;
    
    if( doCreate )
    {
      index = map.tripMarkerLayers.length;
    } // end if
    else
    {
      index = map.tripMarkerLayers.length - 1;
    } // end else
    
    map.tripMarkerLayers[ index ] = new OpenLayers.Layer.GML
    (
      map.nameTMLayers[ index], 
      map.tripMarkerURLs[ index ],
      {
        format:               OpenLayers.Format.KML,
        formatOptions: 
        {
          extractStyles:      true,
          extractAttributes:  true
        },
        eventListeners: 
        { 
          "featuresadded": map.onMarkerAdded 
        }
      }
    );
      
    // add the marker layer
    map.OLMap.addLayer( map.tripMarkerLayers[ index ] );

    // add the connecting line
    map.routeLayers[ index ] = new OpenLayers.Layer.GML
    (
      map.nameRLayers[ index ],
      map.routeURLs[ index ],
      {
        format:               OpenLayers.Format.KML,
        formatOptions: 
        {
          extractStyles:      true,
          extractAttributes:  true
        } 
      }
    );
    map.OLMap.addLayer( map.routeLayers[index]  );

    map.ctrlMarkerSel = new OpenLayers.Control.SelectFeature
    (
      map.tripMarkerLayers[ index ],
      { 
        onSelect:   map.onSelectMarker, 
        onUnselect: map.onUnselectMarker
      }
    );

    map.OLMap.addControl( map.ctrlMarkerSel );
    map.ctrlMarkerSel.activate();
    
    map.zoomInToFeature();
  }; // end function tripReplay
  
  // define the WMS layer and add it to the OLMap object
  // create a google layer if it is defined as such
  map.createWMS = function()
  {
    var layer = null;
    
    if( map.isGoogleMap )
    {
      // define a google street map layer
      if( map.googleMapType != null )
      {
        layer = new OpenLayers.Layer.Google
        (
          map.name,
          {
            'isBaseLayer':      true,
            'visibility':       map.visibility,
            'transitionEffect': map.transitionEff,
            displayOutsideMaxExtent: true,
            type:               map.googleMapType,
            bbox:               map.bbox,
            width:              map.width, 
            height:             map.height,
            numZoomLevels:      map.zoomLevels,
            'reproject':        true
          }
        );

      } // end if
      else
      {
        layer = new OpenLayers.Layer.Google
        (
          map.name,
          {
            'isBaseLayer':      true,
            'visibility':       map.visibility,
            'transitionEffect': map.transitionEff,
            crs:                map.crs, 
            bbox:               map.bbox,
            width:              map.width, 
            height:             map.height,
            numZoomLevels:      map.zoomLevels,
            'reproject':        true
          }
        );
      } // end else
      
    } // end if
    else
    {
      layer = new OpenLayers.Layer.WMS
      (
        map.name,
        map.url,
        { 
          request:            map.request, 
          layers:             map.layers,
          styles:             map.styles, 
          crs:                map.crs, 
          bbox:               map.bbox,
          width:              map.width, 
          height:             map.height, 
          format:             map.format, 
          version:            map.version,
          transparent:        map.transparent,
          opacity:            map.opacity,
          attribution:        map.attribution,
          usertoken:          map.usertoken,
          buffer:             map.buffer
        },
        { 
          'isBaseLayer':      true,
          'visibility':       map.visibility,
          'transitionEffect': map.transitionEff
        }, 
        { 
          'reproject':        map.reproject 
        }
      );
    } // end else
    
    // add the layer to the OLMap object
    if( map.OLMap != null )
    {
      map.OLMap.addLayer( layer );
    } // end if
  
    map.OLMap.zoomToMaxExtent();
  }; // end function createWMS
  
  map.deactivateControls = function()
  {
    map.ctrlZoneModify.deactivate();
    map.ctrlZonePolygon.deactivate();
    map.ctrlZoneSel.deactivate();
  }
  
  map.getTimezone = function( lat, lon )
  {
    var url     =  map.timezoneURL + "?request=timezone&lat=" + lat + "&lon=" + lon;
    var xml     = httpGet( url );
    var result  = -1;
    
    // process the result if it exists
    if( xml.length > 0 ) 
    {
      var startTag  = "<dstOffset>";
      var endTag    = "</dstOffset>";
      var start     = xml.indexOf( startTag );
      var end       = xml.indexOf( endTag );
      var value     = result;
      
      alert( "xml=" + xml + "; start=" + start + " end=" + end + "; value=" + value );
      
      if( (start != -1) && (end != -1) )
      {
        value   = xml.substring( start + startTag.length, end );
        result  = value;
      } // end if
      
    } // end if
    
    return result;
  }; // end function getTimezone
  
  // this method will redraw all the layers
  map.redraw = function()
  {
    var i = 0;
    
    // redraw the route layers if they have been defined
    if( map.routeLayers != null )
    {
      for( i = 0; i < map.routeLayers.length; i += 1 )
      {
        var layer  = map.routeLayers[ i ];
        if( layer != null )
        {
          layer.display( false );
          layer.display( true );
        } // end if
      } // end for
    } // end if
    
    // redraw the marker layers if they have been defined
    if( map.markerLayers != null )
    {
      for( i = 0; i < map.markerLayers.length; i += 1 )
      {
        var layer  = map.markerLayers[ i ];
        if( layer != null )
        {
          layer.display( false );
          layer.display( true );
        } // end if
      } // end for
    } // end if
    
    // redraw the zone layers if they have been defined
    if( map.zoneLayers != null )
    {
      for( i = 0; i < map.zoneLayers.length; i += 1 )
      {
        var layer  = map.zoneLayers[ i ];
        if( layer != null )
        {
          layer.display( false );
          layer.display( true );
        } // end if
      } // end for
    } // end if
  }; // end function redraw
  
    
  // define an open layers map
  map.OLMap = new OpenLayers.Map
  (
    map.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()
      ],
      numZoomLevels: map.zoomLevels,
      eventListeners: 
      {
        "changelayer":      map.layerChanged,
        "changebaselayer":  map.baseLayerChanged
      }
    }
  );
  
  return map;  
}

function createNetstarMap( mapObj, includeNetstar )
{
  if( includeNetstar )
  {
    mapObj.name           = CONST_NETSTAR_MAP;
    mapObj.isGoogleMap    = false;
    mapObj.googleMapType  = null;
    mapObj.createWMS();
  } // end if
}

function createGoogleStreets( mapObj, includeGoogleStreets )
{
  if( includeGoogleStreets )
  {
    mapObj.name           = CONST_GOOGLE_STREETS;
    mapObj.isGoogleMap    = true;
    mapObj.googleMapType  = null;
    mapObj.createWMS();
  } // end if
}

function createGooglePhysical( mapObj, includeGooglePhysical )
{
  if( includeGooglePhysical )
  {
    mapObj.name           = CONST_GOOGLE_PHYSICAL;
    mapObj.isGoogleMap    = true;
    mapObj.googleMapType  = G_PHYSICAL_MAP;
    mapObj.createWMS();
  }
}

function createGoogleSatellite( mapObj, includeGoogleSatellite )
{
  if( includeGoogleSatellite )
  {
    mapObj.name           = CONST_GOOGLE_SATELLITE;
    mapObj.isGoogleMap    = true;
    mapObj.googleMapType  = G_SATELLITE_MAP;
    mapObj.createWMS();
  }
}

function createGoogleHybrid( mapObj, includeGoogleHybrid )
{
  if( includeGoogleHybrid )
  {
    mapObj.name           = CONST_GOOGLE_HYBRID;
    mapObj.isGoogleMap    = true;
    mapObj.googleMapType  = G_HYBRID_MAP;
    mapObj.createWMS();
  }
}

function showMap( mapObj, includeNetstar, includeGoogleStreets, includeGooglePhysical, includeGoogleSatellite, includeGoogleHybrid )
{
  if( mapObj.selectedMap == CONST_GOOGLE_STREETS )
  {
    createGoogleStreets( mapObj, includeGoogleStreets );
    createNetstarMap( mapObj, includeNetstar );
    createGooglePhysical( mapObj, includeGooglePhysical );
    createGoogleSatellite( mapObj, includeGoogleSatellite );
    createGoogleHybrid( mapObj, includeGoogleHybrid );
  } // end if
  else if( mapObj.selectedMap == CONST_GOOGLE_PHYSICAL )
  {
    createGooglePhysical( mapObj, includeGooglePhysical );
    createNetstarMap( mapObj, includeNetstar );
    createGoogleStreets( mapObj, includeGoogleStreets );
    createGoogleSatellite( mapObj, includeGoogleSatellite );
    createGoogleHybrid( mapObj, includeGoogleHybrid );
  } // end if
  else if( mapObj.selectedMap == CONST_GOOGLE_SATELLITE )
  {
    createGoogleSatellite( mapObj, includeGoogleSatellite );
    createNetstarMap( mapObj, includeNetstar );
    createGooglePhysical( mapObj, includeGooglePhysical );
    createGoogleStreets( mapObj, includeGoogleStreets );
    createGoogleHybrid( mapObj, includeGoogleHybrid );
  } // end if
  else if( mapObj.selectedMap == CONST_GOOGLE_HYBRID )
  {
    createGoogleHybrid( mapObj, includeGoogleHybrid );
    createNetstarMap( mapObj, includeNetstar );
    createGoogleSatellite( mapObj, includeGoogleSatellite );
    createGooglePhysical( mapObj, includeGooglePhysical );
    createGoogleStreets( mapObj, includeGoogleStreets );
  } // end if
  else if( mapObj.selectedMap == CONST_NETSTAR_MAP )
  {
    createNetstarMap( mapObj, includeNetstar );
    createGoogleHybrid( mapObj, includeGoogleHybrid );
    createGoogleSatellite( mapObj, includeGoogleSatellite );
    createGooglePhysical( mapObj, includeGooglePhysical );
    createGoogleStreets( mapObj, includeGoogleStreets );
  } // end if
}

function WebMapServerObject( divID, server, width, height, selection, includeNetstar, includeGoogleStreets, includeGooglePhysical, includeGoogleSatellite, includeGoogleHybrid )
{
  var mapObj = null;
  
  // ensure the divID element exists on the page
  if( document.getElementById( divID ) == null )
  {
    //alert( "Map container not found" );
  } // end if
  else
  {
    mapObj  = MapObject();
    
    mapObj.placeholder  = divID;
    mapObj.url          = server;
    mapObj.width        = width;
    mapObj.height       = height;
    mapObj.selectedMap  = selection;
    
    showMap( mapObj, includeNetstar, includeGoogleStreets, includeGooglePhysical, includeGoogleSatellite, includeGoogleHybrid );
  } // end else
  
  return mapObj;
}

function ZoneButton( name, url, handler, alt, css, enabled )
{
  var zoneButton  = new Object();
  
  zoneButton.imageURL = url;
  zoneButton.onClick  = handler;
  zoneButton.hint     = alt;
  zoneButton.style    = css;
  zoneButton.name     = name;
  zoneButton.source   = "";
  zoneButton.enabled  = enabled;  
  
  zoneButton.build = function()
  {
    // define source if the button is enabled otherwise return an empty string
    if( zoneButton.enabled )
    { 
      zoneButton.source = '<img id="' + zoneButton.name + '" style="' + zoneButton.style + '" onclick="' + zoneButton.onClick + '" src="' + zoneButton.imageURL + '" alt="' + zoneButton.hint + '"/>';
    } // end if
    else
    {
      zoneButton.source = "";
    } // end else
  }; // end function draw
  
  zoneButton.build();
  
  return zoneButton;
}




function ZonePanel( map, enableNavigate, enableSelect, enableGoZone, enableNoGoZone, enableSave )
{
  var zone = new Object();
  
  zone.btnNavigate    = ZoneButton( "btnNavigateMap", "img/navigate.png", null, "Navigate the map", "", enableNavigate );
  zone.btnSelectZone  = ZoneButton( "btnSelectZone", "img/select.png", null, "Select a zone on the map", "", enableSelect );
  zone.btnNewGoZone   = ZoneButton( "btnNewGoZone", "img/gozone.png", null, "Draw a new go zone on the map", "", enableGoZone );
  zone.btnNewNoGoZone = ZoneButton( "btnNewNoGoZone", "img/nogozone.png", null, "Draw a new no-go zone on the map", "", enableNoGoZone );
  zone.btnSave        = ZoneButton( "btnSaveZone", "img/save.png", null, "Save the drawn zone to the database", "", enableSave );
  zone.toolbarName    = "zonetoolbar";
  zone.map            = map;
  zone.action         = "navigate";
  zone.creationCount  = 0;
  
  zone.draw = function()
  {
    var div = document.getElementById( zone.toolbarName );
    
    // ensure div exists
    if( div == null )
    {
      alert( "Zone toolbar container was not found" );
      return;
    } // end if
    
    div.innerHTML = zone.btnNavigate.source + zone.btnSelectZone.source + zone.btnNewGoZone.source + zone.btnNewNoGoZone.source + zone.btnSave.source;
  }; // end function draw
  
  zone.activateNavigation = function()
  {
    zone.action = "navigate";
    zone.map.deactivateControls();
    zone.map.zoneCreating = false;    
    
    zone.map.redraw();
  }
  
  zone.activateSelectZone = function()
  {
    zone.action = "selectzone";
    zone.map.deactivateControls();
    zone.map.ctrlZoneSel.activate();
    zone.map.zoneCreating = false;    
    
    zone.map.redraw();
  }
  
  zone.activateNewGoZone = function()
  {
    // ensure that the limit has not been reached
    if( zone.map.isLimitReached() )
    {
      alert( "Zone creation limit has been reached; the maximum number of zones allowed to be created at a time is " + map.zoneCreateLimit );
      zone.activateNavigation();      
   } // end if
    else
    {
      zone.action = "newgozone";
      zone.map.deactivateControls();
      zone.map.ctrlZonePolygon.activate();
      zone.map.zoneCreating = true;    
    } // end else
  
  }
  
  zone.activateNewNoGoZone = function()
  {
    // ensure that the limit has not been reached
    if( zone.map.isLimitReached() )
    {
      alert( "Zone creation limit has been reached; the maximum number of zones allowed to be created at a time is " + map.zoneCreateLimit );
      zone.activateNavigation();      
    } // end if
    else
    {
      zone.action = "newnogozone";
      zone.map.deactivateControls();
      zone.map.ctrlZonePolygon.activate();
      zone.map.zoneCreating = true;    
    } // end else
  }
  
  zone.activateSaveZone = function()
  {
    zone.action = "savezone";
    zone.map.deactivateControls();
    zone.map.saveZones();
    zone.map.zoneCreating = false;    
     
    zone.map.redraw();
 }
  
  return zone;
}