    // ********************************************************************
    // ********************************************************************
    // Globals
    var do_short_run = 0;
    var short_run_length = 200;

    var g_tracks = new Array;           // g_tracks in an array of tracks.  Each track is an array of ObjGPSPlot (containing Lat, Lon, Time info)
    var g_track_timer_index = new Array // Pointer into the g_tracks array for the current display point
    var g_wpts = new Array;             // g_wpt is an array of waypoints.  Each waypoint is an ObjGPSWaypoint (containing Lat, Lon, Name)
    var g_rtepts = new Array;           // g_rtepts is an array of points in the route .  Each point is an GLatLng

    var g_trackTimer = 0;
    var g_trackTimerDelta = 0;
    var g_trkTimer;
    var g_trkSpeed = 30; //30; // Timer increment in seconds

    var g_NLim = new Number(0);
    var g_SLim = new Number(0);
    var g_WLim = new Number(0);
    var g_ELim = new Number(0);

    // Declare the map itself
    var map;

    // Declare the XML Document for the tracks etc.
    var xmlDoc;


    // Define some icons
    var icons = new Array;
    var iconWaypoint;    

    function resetSystem()
    {
      var thisFunct = "greplay.js:resetSystem(): ";
      debug(thisFunct + "entry");
      // Arrays
      while (g_tracks.length > 0)
      {
        debug(thisFunct + "g_tracks.length :" + g_tracks.length);
        debug(thisFunct + "Remove elements from g_tracks with splice :");
        g_tracks[g_tracks.length-1].splice(0,g_tracks[g_tracks.length - 1].length);
        g_tracks.splice(g_tracks.length - 1,1); // Remove the last element
      }
      debug(thisFunct + "Final g_tracks.length :" + g_tracks.length);

      g_track_timer_index.splice(0,g_track_timer_index.length);
      g_wpts.splice(0,g_wpts.length);
      g_rtepts.splice(0,g_rtepts.length);


//      while(g_track_timer_index.length > 0){ g_track_timer_index.pop};
      debug(thisFunct + "arrays cleared");

//      xmlDoc = "";

      // Vars
      g_trackTimer = 0;
      g_trackTimerDelta = 0;
      clearTimeout(g_trkTimer);
//      g_trkTimer;
      g_trkSpeed = 30; //30; // Timer increment in seconds
      g_NLim = 0;
      g_SLim = 0;
      g_WLim = 0;
      g_ELim = 0;

      debug(thisFunct + "exit");
    }


    function buildIcons()
    {
      var thisFunct = "greplay.js:buildIcons(): ";

      var baseIcon = new GIcon();

      // Build an arry of icons to be used later
      baseIcon.iconSize=new GSize(3,3);
      baseIcon.iconAnchor=new GPoint(1,1);
      baseIcon.infoWindowAnchor=new GPoint(1,1);
      baseIcon.imageMap=[(0,0),(0,2),(2,2),(2,0)];

      icons.push (new GIcon(baseIcon, "./common/yellow.png", null, ""));
      icons.push (new GIcon(baseIcon, "./common/green.png", null, ""));
      icons.push (new GIcon(baseIcon, "./common/red.png", null, ""));
      icons.push (new GIcon(baseIcon, "./common/blue.png", null, ""));

      icons.push (new GIcon(baseIcon, "./common/orangeCircle.png", null, ""));
      icons.push (new GIcon(baseIcon, "./common/violetCircle.png", null, ""));
      icons.push (new GIcon(baseIcon, "./common/greenCircle.png", null, ""));
      icons.push (new GIcon(baseIcon, "./common/redCircle.png", null, ""));


      // Define the waypoint icon      
      var waypoint = new GIcon();
      waypoint.iconSize=new GSize(9,9);
      waypoint.iconAnchor=new GPoint(4,4);
      waypoint.infoWindowAnchor=new GPoint(4,4);
      waypoint.imageMap=[(0,0),(0,8),(8,8),(8,0)];
      waypoint.image="./common/waypoint.png";
      
      iconWaypoint = waypoint;
    }

    // ********************************************************************
    // Plot Object Constructor
    // ********************************************************************
    function  ObjGPSPlot(lat, lon, strTime, track_title, icon)
    {
      this.lat = Number(lat);
      this.lon = Number(lon);
      this.tim = strTime;
      this.secs = convSecs(strTime);
      this.time = convTime(this.secs);
      this.marker = new GMarker(new GLatLng(this.lat, this.lon),{ icon:icons[icon], title:track_title + " " + this.time});
    }

    function ObjGPSWaypoint(lat, lon, name, icon)
    {
      this.lat = Number(lat);
      this.lon = Number(lon);
      this.marker = new GMarker(new GLatLng(this.lat, this.lon),{ icon:icon, title:name});
    }



    // Convert time from the XML string format into a numerical format
    function convSecs(GPSTime)
    {
      var thisFunct = "greplay.js:GPSTime(): ";
    
      var year = 0;
      var month = 0;
      var day = 0;
      var hours = 0;
      var mins = 0;
      var secs = 0;
      var seconds;

//      debug(thisFunct + "Entry");

      if(GPSTime.length == 20)
      {
        year = Number(GPSTime.slice(0,4));
        month = Number(GPSTime.slice(5,7));
        day = Number(GPSTime.slice(8,10));
        hours = Number(GPSTime.slice(11,13));
        mins = Number(GPSTime.slice(14,16));
        secs = Number(GPSTime.slice(17,19));
      }

      seconds = (hours * 3600) + (mins * 60) + secs;

//      debug(thisFunct + "convSecs() : Date : " + year + "/" + month + "/" + day + "  " + hours + ":" + mins + ":" + secs + " Seconds =" + seconds);

      return seconds;
    }

    function convTime(t)
    {
      var rS = t%60;
      var rM = ((t%3600) - rS)/60;
      var rH = Math.floor(t/3600) + 1; // BST adjustment
      return (rH<10?"0"+rH:rH) +":" + (rM<10?"0"+rM:rM) + ":" + (rS<10?"0"+rS:rS);  // Fixed length format
    }

    // Put the time onto the users screen, it identifies the time tag in the html below
    function reportTime(t,status)
    {
      document.getElementById("time").innerHTML = "Time : " + convTime(t) + status;
    }


    // Setup the info box
    function ReportInfo(InfoString)
    {
      document.getElementById("info").innerHTML += "<br/>" + InfoString;
    }

    function resetInfo()
    {
      document.getElementById("info").innerHTML = "Track List";
    }


    function resetTrackTimer()
    {
      var thisFunct = "greplay.js:resetTrackTimer(): ";

      // Make sure the timer is stopped
      stopTrackTimer();
    
      // Initialise the timer to the largest 1st value in each of the tracks in g_Tracks
      var i;

      g_trackTimer = 0;     // Reset the track timer
      map.clearOverlays();  // Clear the points from the map
      getWaypoints();       // Draw the waypoints

      // Stop the automatic clock from ticking
      g_trackTimerDelta = 0;
      clearTimeout(g_trkTimer);

      // Now initialise to the start of the latest track
      for(i=0; i<g_tracks.length; i++)
      {
        g_trackTimer = Math.max(Number(g_trackTimer), Number(g_tracks[i][0].secs));
        debug(thisFunct + "track : " + i + " Start time : " + g_tracks[i][0].secs + "  Current track timer : " + g_trackTimer);

        g_track_timer_index[i] = 0; // Reset the latest shown pointer
      }

      reportTime(g_trackTimer, "   Reset");
      debug(thisFunct + "Time =" + g_trackTimer);
    }

    function startTrackTimer()
    {
      var thisFunct = "greplay.js:startTrackTimer(): ";

      g_trackTimerDelta = g_trkSpeed; // Time increment, in seconds
      trackTimer();                   // Call it the first time to get it going
      debug(thisFunct + "Time =" + g_trackTimer);
    }

    function stopTrackTimer()
    {
      var thisFunct = "greplay.js:stopTrackTimer(): ";

      g_trackTimerDelta = 0;
      clearTimeout(g_trkTimer);

      reportTime(g_trackTimer, "   Stopped");
      debug(thisFunct + "Time =" + g_trackTimer);
    }

    // The track timer repeatedly calls itself at intervals, until stopped
    function trackTimer()
    {
      var thisFunct = "greplay.js:trackTimer(): ";
    
      g_trackTimer += g_trackTimerDelta;
      reportTime(g_trackTimer, "   Running");

      // Need to add something incase g_trackTimerDelta is zero
      if (drawTracks() || g_trackTimerDelta == 0)      // Call drawtracks upto the current time, or delta = 0
      {
        reportTime(g_trackTimer, "   Finished");       // Reached the end of a track
      }
      else
      {
        g_trkTimer = setTimeout('trackTimer()',100);   // Keep going!  Keep calling every Xms
      }

      debug(thisFunct + "Time =" + g_trackTimer);
    }


    // Option ***** GPolyLine ***
    // We can use GPolyline to draw the track
    // We need to add the GPolyline to the map before use (we can have one for each track)
    // In this function, we can add the appropriate vertices to the GPolyLine
    // This should extend the drawing
    // Look at http://econym.googlepages.com/example_cartrip.htm
    // http://code.google.com/apis/maps/documentation/introduction.html
    // http://code.google.com/apis/maps/documentation/overlays.html
    // http://code.google.com/apis/maps/documentation/reference.html
    // http://code.google.com/apis/maps/documentation/reference.html#GMap2
    // http://code.google.com/p/gmaps-utility-library/
    function plotPoint(track, index)
    {
      var thisFunct = "greplay.js:plotPoint(): ";
    
      map.addOverlay(g_tracks[track][index].marker);
      debug(thisFunct + "track:" + track + " index:" + index + "   time:" + g_tracks[track][index].secs);
    }

    function drawTracks()
    {
      var thisFunct = "greplay.js:drawTracks(): ";

      debug(thisFunct + "entry : ********** NEW TIMER ITERATION ****************   Global display timer:" + g_trackTimer);

      var allTracksFinished = true; // Assume that we are past the end of all of the tracks, change if we find different

      for (i=0;i<g_tracks.length;i++)
      {
        debug(thisFunct + "Track:" + i + " Track length:"+ g_tracks[i].length +" Current index:"+g_track_timer_index[i]);
      }

      for(track = 0; track < g_tracks.length; track++)
      {
        debug(thisFunct + "Starting from track:" + track + " index:" + g_track_timer_index[track]);

        finished = false;
        // Move through the track, using g_track_timer_index[track], until we catch up with the timer or finish
        while(!finished)
        {
          if (g_track_timer_index[track] >= g_tracks[track].length-1)
          {
            // Run out of points to plot in this track
            finished = true;
            debug(thisFunct + "Track:" + track + "finished, beyond points in track");
          }
          else if ( g_trackTimer < g_tracks[track][g_track_timer_index[track]].secs )
          {
            // This point is beyond the current marker point
            finished = true;
            debug(thisFunct + "Track:" + track + " finished, beyond track timer");
          }
          else
          {
            g_track_timer_index[track]++;
            plotPoint(track, g_track_timer_index[track]);
            allTracksFinished = false;
          }
        }
      }
      debug(thisFunct + "exit");
      return allTracksFinished;
    }


    function getRoutes()
    {
      var thisFunct = "greplay.js:getRoutes(): ";
      var lar, lon = new Number(0);

      // Get the routes in the file
      var xmlrte = xmlDoc.getElementsByTagName('rte');

      if(xmlrte.length) // Were there any routes?  If not, skip this or it will hang
      {
        // Get the points from the routes, assume just the first route is valid
        var xmlrtepts = xmlrte[0].getElementsByTagName('rtept');
        debug(thisFunct + "No. of points in route = " + xmlrtepts.length);

        // For each point in the route
        for(i=0; i<xmlrtepts.length; i++)
        {
          // Lat and Lon are attributes
          lat = xmlrtepts[i].attributes.getNamedItem("lat").value;
          lon = xmlrtepts[i].attributes.getNamedItem("lon").value;

          debug(thisFunct + "lat: " + lat + " lon: " + lon);
          g_rtepts.push(new GLatLng(lat, lon, false));
        }

        // Now plot the route
        var Route = new GPolyline(g_rtepts, "#808080", 3, 0.5);
        map.addOverlay(Route);
      }

     debug(thisFunct + "Exit");
    }


    function getWaypoints()
    {
      var thisFunct = "greplay.js:getWaypoints(): ";
      var lat, lon = new Number(0);

      // Get each waypoint in the file
      var xmlwpt = xmlDoc.getElementsByTagName('wpt');
      debug(thisFunct + "No. of waypoints = " + xmlwpt.length);

      // For each waypoint
      for(i=0; i<xmlwpt.length; i++)
      {
        // Lat and Lon are attributes
        lat = xmlwpt[i].attributes.getNamedItem("lat").value;
        lon = xmlwpt[i].attributes.getNamedItem("lon").value;

        n = xmlwpt[i].getElementsByTagName('name');
        name = n[0].firstChild.nodeValue;

        debug(thisFunct + "name: " + name + " lat: " + lat + " lon: " + lon);
        g_wpts.push(new ObjGPSWaypoint(lat, lon, name, iconWaypoint));
      }

      // Now plot the waypoints
      for(i=0; i<g_wpts.length; i++)
      {
        map.addOverlay(g_wpts[i].marker);
        debug(thisFunct + "Plotting waypoint : " + i);
      }

      // Lets do the routes too
      getRoutes();

      debug(thisFunct + "exit");
    }


    function getTracks()
    {
      var thisFunct = "greplay.js:getTracks(): ";
      var t, n;
      var i, j, k;    // Keep these local
      var lat, lon = new Number(0);
      var strTime;
      var FirstPass = 1;
      debug("getTracks() : Entry");

      // Get each track in the file
      var xmltracks = xmlDoc.getElementsByTagName('trk');
      debug(thisFunct + "No. of tracks = " + xmltracks.length);

      // For each track
      for(i=0; i<xmltracks.length; i++)
      {
        // Get the name of the track
        var n = xmltracks[i].getElementsByTagName('name');
        var track_title = n[0].firstChild.nodeValue;
        debug(thisFunct + "Track:" + i + " Name:" + track_title);
        ReportInfo("<img src=\"" + icons[i].image + "\"/>" + " " + track_title);

        // Get the track segments in the track
        var trackseg = xmltracks[i].getElementsByTagName('trkseg');
        var y = trackseg.length;
        debug(thisFunct + "Track " + i + " contains " + y + " track segments");

        // Create a new track and on the global tracks array
        // We add the position points later
        g_tracks[i] = new Array;

        // For each track segment, get the track points (multisegment tracks)
        for(j=0; j<trackseg.length; j++)
        {
          var trackpts = trackseg[j].getElementsByTagName('trkpt');

          // For each track point, get the time and co-ordinates
          debug(thisFunct + "Track : " + i + "  Segment : " + j);

          kLimit = trackpts.length;
          if (do_short_run) kLimit = Math.min(short_run_length, kLimit);  // Trim for development, but not beyond the end
          debug(thisFunct + "Looping on " + kLimit + " elements in track segment")

          // Iterate the track points for each track
          for (k=0; k<kLimit; k++)
          {
            // Lat and Lon are attributes
            lat = trackpts[k].attributes.getNamedItem("lat").value;
            lon = trackpts[k].attributes.getNamedItem("lon").value;

            debug(thisFunct + "Pt " + k + " " + lat + " " + lon);


            // We are traversing the track, lets be useful and get the race area bound too.
            if (FirstPass)
            {
              debug(thisFunct + "First pass, resetting bounds to minimum");
              g_NLim = lat;
              g_SLim = lat;
              g_ELim = lon;
              g_WLim = lon;
              FirstPass = 0;
            }
            else
            {
              if (g_NLim < lat) g_NLim = lat;
              if (g_SLim > lat) g_SLim = lat;
              if (g_ELim > lon) g_ELim = lon;
              if (g_WLim < lon) g_WLim = lon;
            }

            // Time is a tag (but may not exist in all tracks)
            t = trackpts[k].getElementsByTagName('time');
            if (t.length > 0)
            {
              strTime = t[0].firstChild.nodeValue;
            }
            else
            {
              strTime = "2009-06-20T08:00:00Z"; // Dummy entry
            }
            debug(thisFunct + "Time = " + strTime);

            // Add this point to the curent track list
            g_tracks[i].push(new ObjGPSPlot(lat, lon, strTime, track_title, i));

          } // End of trackpoints iteration
          debug(thisFunct + "Track point iteration of segment finished");

        }// End of track segments iteration
        debug(thisFunct + "Iteration of segments in track finished");
      }// End of track iteration
      debug(thisFunct + "N:" + g_NLim + "  S:" + g_SLim + "  E:" + g_ELim + "  W:" + g_WLim);
      debug(thisFunct + "Iteration of tracks finished");

      debug(thisFunct + "complete; exit");
    }

