/*

  daylightmap.com/index.js
  
  Copyright 2006 Udell Enterprises, Inc

*/

var map;
var title;
var linkTo;
var mapDiv;
var mainDiv;
var titleDiv;
var adsDiv;
var optionsDiv;
var flagDiv;
var optionsBtn;
var optionsContent; 
var sunCbx;
var monthSelect;
var daySelect;
var hourSelect;
var minuteSelect;
var tzSelect;
var selector;
var markerStyle;
var placer = new Array(2);
var saveCbx;
var markerList;
var meridians;

var daylight;
var clickMarker = null;
var expanded = false;
var flagsShown = false;
var clicking = false;
var timerId = null;
var _refreshInterval = 2 * 60 * 1000;
var currentTimezone;
var _iconColors = new Array('red', 'orange', 'blue', 'gray', 'brown', 
                            'purple', 'white', 'green', 'yellow', 'black');
var _maxPoints = _iconColors.length;
var _maxLat = 85.0511287798066;
var icons = new Array(_maxPoints);
var clockIcons = {digital: null, analog: null};
var points = new Array();
var nextId = 0;
var selected = null;
var _loading = 'Loading...';
var shownInstructions = false;
var hideChrome = false;
var panControl;
var typeControl;
var scaleControl;
var sunIcon;
var sunMarker = null;
var sunOnMap = false;
var sunCoords = false;
var cookieExpire = cookieDate(new Date(Number(new Date()) + 90 * _mSecPerDay));
var refreshing = false;
var tooltip = false;

addEventHandler(window, 'load',     init);addEventHandler(window, 'unload',   GUnload);
addEventHandler(window, 'onresize', windowResize);

GMap2.prototype.fromLatLngToContainerPixel = function (coords)
{
   var originPixel = this.fromLatLngToDivPixel(this.fromContainerPixelToLatLng(new GPoint(0, 0)));
   var targetPixel = this.fromLatLngToDivPixel(coords);

   return new GPoint(targetPixel.x - originPixel.x, targetPixel.y - originPixel.y);
};

GLatLng.prototype.directionFrom = function (other)
{
  // Returns the shortest direction from the other lat/lon coords to this one, either
  //  1 = East
  //  0 = Same Longitude
  // -1 = West

  if (other.lng() == this.lng())
    return 0;
  else
  {
    var difference = other.lng() - this.lng()
    while (Math.abs(difference) > 180) 
    {
      if (difference > 0)
        difference -= 360;
      else
        difference += 360;
    }

    if (difference > 0)
      return 1;
    else
      return -1;
  }
};

//  A Point of Interest is mostly a wrapper for a marker that the user places on the map.
function pointOfInterest(name, coords)
{
  // Assign the next available point ID to this one, and increment for next time.
  this.id = nextId++;

  // Init time-related properties
  this.tzOffset = null;
  this.sunrise  = null;
  this.sunset   = null;

  if (!daylightMap._nightMode)
  {
    // Create the GMap marker itself
    this.coords = coords;
    var listImage = this.setIcon();
  
    markerList.innerHTML += 
      '<div class="list_line" id="list_' + this.id + '" ' +
           'onclick="if (getTarget(event).id != \'delete_' + this.id + '\') showPointInfo(' + this.id + ')">' +
        '<p class="tools">' +
          '<a href="/" id="delete_' + this.id + '" onclick="deletePoint(' + this.id + '); return false">' + l10n['delete'] + '</a>' + 
        '</p>' +
        '<h4>' +
          '<img align="left" src="' + listImage + '"  id="icon_' + this.id + '"/>' +
          '<span id="name_' + this.id + '"></span>' +
        '</h4>' +
        '<p id="time_' + this.id + '">' + this.localTime() + '</p>' +
      '</div>';
  
    // Additional setup is done by other methods
    this.setName(name);
    this.retrieveInfo();
  }
};

pointOfInterest.prototype.setIcon = function()
{
  if (this.marker)
    map.removeOverlay(this.marker);

  var icon = getIcon();
  this.marker = new GMarker(this.coords, {icon: icon, draggable: true});

  // Assign event handlers
  var thisId = this.id;
  GEvent.addListener(this.marker, 'click', function(){showPointInfo(thisId)});
  GEvent.addListener(this.marker, 'dragend', function(){markerMoveEnd(thisId)});

  // Add the marker to the map
  map.addOverlay(this.marker);
  this.marker.disableDragging();

  // Add this point to the list in the Options bar

  var gifImage = icon.image;
  if (gifImage.indexOf('/clock/') == -1)
    gifImage = gifImage.replace('png', 'gif');

  return gifImage;
};

pointOfInterest.prototype.setName = function(name)
{
  // Assign a name to this point

  document.getElementById('name_' + this.id).innerHTML = name;
  this.name = name;
  
  setTimeout('makeLink()', 10);
};

pointOfInterest.prototype.refreshInfo = function()
{
  // Refresh the time data for this point
  this.tzOffset = null;
  this.sunrise  = null;
  this.sunset   = null;
  
  if (selected == this.id)
    this.showInfo();
    
  this.retrieveInfo();
};

pointOfInterest.prototype.retrieveInfo = function()
{
  // Retrieve the point's time data from the server
  try 
  {  
    var infoURL = '/point_info.php'
                + '?nPointID=' + this.id
                + '&fLatitude=' + this.marker.getPoint().lat()
                + '&fLongitude=' + this.marker.getPoint().lng()
                + '&dtmWhen=' + Number(getWhen()) / 1000
                + '&nMapOffset=' + getMapOffset();
  
    if (!this.infoRequest)
      this.infoRequest = new xmlRequestor;
  
    // The explicit Function object for onreadystatechange was the most straightforward
    // way I could find to pass this point's ID to the event handler.
    this.infoRequest.open('GET', infoURL, true);
    this.infoRequest.onreadystatechange = new Function("parsePointInfo(" + this.id + ")");
    this.infoRequest.send(null);
  } 
  catch (e) 
  {
    // TODO: handle exception
  }
};

pointOfInterest.prototype.displayTime = function(when)
{
  // Format a time value for display, including date if not today

  var primaryDate = new Date(Number(getWhen()));
  var targetDate = new Date(Number(when) + (getWhen().getTimezoneOffset() + this.tzOffset * 60) * 60 * 1000);

  if (targetDate.getDate() == primaryDate.getDate())
    var timeAdj = '';
  else
    var timeAdj = ', ' + targetDate.toString().substr(0, 10);

  return formatTime(when, this.timeOptions) + timeAdj;
};

pointOfInterest.prototype.localTime = function()
{
  // Returns the local time for this point, formatted for display
  if (this.infoError)
    return 'Error';
  else if (this.tzOffset == null)
    return _loading;
  else
  {
    var result = this.displayTime(getWhen()) + ' (GMT' + displayOffset(this.tzOffset);

    result += ')';

    return result;
  }
};

pointOfInterest.prototype.showInfo = function()
{
  // Display a GMap InfoWindow for this point
  
  // Format sunrise time for display
  if (this.infoError || !this.sunrise)
    return;
  else
  {
    if (this.sunrise > getWhen())
      var untilSunrise = formatDelta(this.sunrise - getWhen()) + ' ' + l10n.from_now;
    else
      var untilSunrise = formatDelta(this.sunrise - getWhen()) + ' ' + l10n.ago;
    
    untilSunrise = this.displayTime(this.sunrise) + ' (' + untilSunrise + ')';
  }    

  // Ditto for sunset
  if (this.infoError || !this.sunset)
    return;
  else
  {
    if (this.sunset > getWhen())
      var untilSunset = formatDelta(this.sunset - getWhen()) + ' ' + l10n.from_now;
    else
      var untilSunset = formatDelta(this.sunset - getWhen()) + ' ' + l10n.ago;
    
    untilSunset = this.displayTime(this.sunset) + ' (' + untilSunset + ')';
  }    

  // And length of daylight (or night, as appropriate)
  if (!this.sunrise || !this.sunset)
    return;
  else
  {
    var dayLength = (this.sunset - this.sunrise);

    if (dayLength < -_mSecPerDay)
      // Arctic night
      dayLength = l10n.length_night + ': ' + formatDelta(this.sunrise - this.sunset);
    else
    {
      if (this.sunset > this.sunrise)
        dayLength = l10n.length_day + ': ' + formatDelta(this.sunset - this.sunrise);
      else
        dayLength = l10n.length_day + ': ' + formatDelta(_mSecPerDay - (this.sunrise - this.sunset));
    }
  }
  
  var locationHTML =
    '<div id="info_window">' +
      '<h4>' +
        '<span id="info_name_' + this.id + '" ' +
              'onclick="renamePoint(' + this.id + ', \'info\')">' + this.name + '</span>' +
      '</h4>' +
      '<p class="info_line" id="lat_lon">' + this.latLon() + '</p>' + 
      '<p class="info_line">' + l10n.local_time + ': ' + this.localTime() + '</p>' +
      '<p class="info_line">' + l10n.sunrise + ': ' + untilSunrise + '</p>' +
      '<p class="info_line">' + l10n.sunset + ': ' + untilSunset + '</p>' +
      '<p class="info_line">' + dayLength + '</p>' +
      '<p class="info_line tools">' +
        '<a href="/" onClick="movePoint(' + this.id + '); return false">' + l10n.move + '</a> &middot; ' + 
        '<a href="/" onClick="renamePoint(' + this.id + '); return false">' + l10n.rename + '</a> &middot; ' + 
        '<a href="/" onClick="deletePoint(' + this.id + '); return false">' + l10n['delete'] + '</a>' + 
      '</p>' +
    '</div>';

  this.marker.openInfoWindowHtml(locationHTML);

  if (tooltip)
    tooltip.hide();

  setTimeout('selectList(' + this.id + ')', 1);
};

pointOfInterest.prototype.latLon = function()
{
  // Format the point's coordinates for display
  var coords = this.marker.getPoint();
  var latLon = '';
  
  latLon += Math.abs(coords.lat()).toFixed(6) + '&deg; ';
  if (coords.lat() < 0)
    latLon += l10n.s;
  else
    latLon += l10n.n;
  
  latLon += ', ' + Math.abs(coords.lng()).toFixed(6) + '&deg; ';
  if (coords.lng() < 0)
    latLon += l10n.w;
  else
    latLon += l10n.e;
    
  return latLon;
};

pointOfInterest.prototype.refreshIcon = function()
{
  if (this.infoError)
    return;

  var listTime = document.getElementById('icon_' + this.id);
  var clockStyle = valueOfField(markerStyle);
  if (clockStyle != 'color')
  {
    // Update the clock icon
    var filename = formatTime(getWhen(), {delimiter: '_', 
                                          offset: -this.tzOffset * 60, 
                                          military: true, 
                                          seconds: false,
                                          padHours: false})
    filename = '/clock/' + clockStyle + '/' + filename + '.png';
    this.marker.setImage(filename);

    if (listTime)
      listTime.src = filename;

    if (clockStyle == 'analog')
    {
      GEvent.addListener(this.marker, 'mouseover', new Function('pointMouseOver(' + this.id + ')'));
      GEvent.addListener(this.marker, 'mouseout',  new Function('pointMouseOut(' + this.id + ')'));
    }
  }
  else
    if (listTime)
      listTime.src = this.marker.getIcon().image.replace('png', 'gif');
};

function showPointInfo(id)
{
  // A wrapper function to call showInfo given the point ID

  if ((id == selected) &&
      document.getElementById('info_window'))
    map.closeInfoWindow();

  var point = getPoint(id);
  if (point)
    point.showInfo();
};

function parsePointInfo(id)
{
  // Process the time-info server response for the given point
  
  var point = getPoint(id);

  if (point)
  {
    if (point.infoRequest.readyState == 4)
    {
      // 4 means the request is completed
      try
      {
        var xmlDoc = point.infoRequest.responseXML;

        if (getText(xmlDoc.documentElement.getElementsByTagName('error')) != '')
          point.infoError = true;
        else
        {
          point.infoError = false;

          // Extract the data from the XML  
          point.tzOffset    = parseFloat(getText(xmlDoc.documentElement.getElementsByTagName('offset')));
          point.sunrise     = new Date(parseInt(getText(xmlDoc.documentElement.getElementsByTagName('sunrise_unix'))) * 1000);
          point.sunset      = new Date(parseInt(getText(xmlDoc.documentElement.getElementsByTagName('sunset_unix'))) * 1000);
          point.timeOptions = {offset: -point.tzOffset * 60, military: false, seconds: false};
  
          // Update the option-bar marker list
          var listTime = document.getElementById('time_' + point.id);
          if (listTime)
            listTime.innerHTML = point.localTime();
  
          point.refreshIcon();

          if (selected == point.id)
            // The point's info window is open, so update that too
            point.showInfo();
  
          // Update the position of the sun  
          if (points[0].id == point.id)
            sunMove(xmlDoc);
        }
      }
      catch(error)
      {
        point.infoError = true;
      }
    }
  }
};
    
function getPoint(id)
{
  // Utility function to return the point object for the given ID

  for (var n = 0; n < points.length; n++)
    if (points[n].id == id)
      return points[n];

  return null;
};

function wheelZoom(event) 
{
  if (event.cancelable)
    event.preventDefault();
 
  if ((event.detail || -event.wheelDelta) < 0)
    map.zoomIn()
  else
    map.zoomOut();

  return false;
}


function init()
{
  // Main routine to set up the map, etc.
  
  // Init HTML element pointers for later use
  mapDiv         = document.getElementById('map');
  mainDiv        = document.getElementById('main');
  titleDiv       = document.getElementById('title');
  linkTo         = document.getElementById('link_to');
  adsDiv         = document.getElementById('adsense');
  optionsDiv     = document.getElementById('options');
  flagDiv        = document.getElementById('flag');
  optionsBtn     = document.getElementById('options_btn')
  optionsContent = document.getElementById('options_content');
  sunCbx         = document.getElementById('sun');
  monthSelect    = document.getElementById('month');
  daySelect      = document.getElementById('day');
  hourSelect     = document.getElementById('hour');
  minuteSelect   = document.getElementById('minute');
  tzSelect       = document.getElementById('timezone');
  selector       = document.getElementById('selector');
  saveCbx        = document.getElementById('save');
  placer[0]      = document.getElementById('placer_outer');
  placer[1]      = document.getElementById('placer_inner');
  markerList     = document.getElementById('marker_list');
  meridians      = document.getElementById('meridians');
  markerStyle    = document.getElementById('marker_style');

  clockIcons.analog = new GIcon();
  clockIcons.analog.image            = '/clock/analog_blank.png';
  clockIcons.analog.iconSize         = new GSize(31, 31);
  clockIcons.analog.iconAnchor       = new GPoint(15, 15);
  clockIcons.analog.infoWindowAnchor = new GPoint(23, 10);

  clockIcons.digital = new GIcon();
  clockIcons.digital.image            = '/clock/digital_blank.png';
  clockIcons.digital.iconSize         = new GSize(39, 18);
  clockIcons.digital.iconAnchor       = new GPoint(18, 9);
  clockIcons.digital.infoWindowAnchor = new GPoint(19, 2);

  document.cookie = 'cookies_enabled=1';
  if (document.cookie.search('cookies_enabled=1') == -1)
    // Browser appears to have persistent cookies disabled
    saveCbx.checked = false;

  if (titleDiv)
  {
    // Browser-specific fixes for title image peccadilloes
    if (navigator.appName == 'Microsoft Internet Explorer')
      fixPNG(document.getElementById('title_img'));
    else if (navigator.appName == 'Netscape')
      document.getElementById('title_nav').style.top = '30px';
  }

  // Init the base time zone
  if (!initTimezone)
  {
    initTimezone = - (new Date().getTimezoneOffset()) / 60;
    syncTimezone(initTimezone);
  }
  currentTimezone = initTimezone;

  if (GBrowserIsCompatible())
  {
    // These event handlers set up the screen layout
    windowResize();
    document.onkeypress = keyHandler;
    
    // Init the map object itself
    mapDiv = document.getElementById('map');
    map = new GMap2(mapDiv, {mapTypes: [G_NORMAL_MAP, G_PHYSICAL_MAP, G_SATELLITE_MAP, G_HYBRID_MAP]});
    map.enableDoubleClickZoom();

    // Map events
    GEvent.addListener(map, 'click',           mapClick);
    GEvent.addListener(map, 'move',            mapMove);
    GEvent.addListener(map, 'moveend',         mapMoveEnd);
    GEvent.addListener(map, 'infowindowopen',  infoWindowOpen);
    GEvent.addListener(map, 'infowindowclose', unSelectList);
    GEvent.addListener(map, 'zoomend',         syncDate);
    GEvent.addListener(map, 'maptypechanged',  makeLink);

    // Init the day/night display
    daylight = new daylightMap.daylightLayer();
    daylight.active = false;
    daylight.addToMap(map);

    // Add standard map controls
    panControl   = new GLargeMapControl();
    typeControl  = new GMapTypeControl();
    scaleControl = new GScaleControl();
    map.addControl(typeControl);
    map.addControl(scaleControl);
    if (titleDiv)
      map.addControl(panControl, new GControlPosition(G_ANCHOR_TOP_LEFT, new GSize(10, 70)));
    else
      map.addControl(panControl);

    // Adjust initial zoom to best-fit the world map into the browser window
    if (initZoom == null)
    {
      if ((mapDiv.offsetWidth * 1.1) > 360 * daylightMap._pixelsPerLonDegree[2])
        initZoom = 2;
      else
        initZoom = 1;

      if (daylightMap._nightMode)
        initZoom++;
    }

    // Init the map display
    map.setCenter(new GLatLng(initLat, initLon), initZoom, initType);
    
    if (!daylightMap._nightMode)
    {
      // Create the sun-position marker
      sunIcon = new GIcon();
      sunIcon.image = '/images/sun.png';
      sunIcon.iconSize = new GSize(25, 25);
      sunIcon.iconAnchor = new GPoint(12, 12);
      sunMarker = new GMarker(new GLatLng(90, 0), {icon: sunIcon, clickable: false});    
    }

    // Set up any points that were passed into the PHP via cookies or URL parms
    initPoints();
    
    if (!daylightMap._nightMode &&
        ((location.search.search('[/?&]d=') == -1) ||
         document.forms['options_content'].elements['current'].checked))
    {
      // Neither night-mode nor a specific date/time being shown => init auto-refresh of terminator
      autoRefresh(true);
    }
    else
    {
      // Show the initial daylight plot    
      refreshDaylight();
    }

    // A bit of dynamic CSS
//    var clientVersion = parseFloat(navigator.vendorSub);
//    if ((navigator.vendor != "Netscape") || 
//        (clientVersion == 0) ||
//        (clientVersion >= 8))
    {
      // Safe to scroll the marker list div. I do this here because this CSS directive crashed NS 7.1
      document.getElementById('marker_list').style.overflow = 'auto';
    }
  }
  else
    alert(l10n.no_gmaps_msg);

//  if (window.location.host.indexOf('dev.') != 0)
//  {
//    // Log the pageview to Google Analytics
//    _uacct = "UA-1556555-1";
//    urchinTracker();
//  }
};

function syncDate()
{
  // Set the on-screen date/time selectors to match the given date object

  var when = getWhen();
  if (!when)
    return;
  
  var n;

  for (n = 0; n < monthSelect.length; n++)
    if (monthSelect.options[n].value == when.getMonth())
    {
      monthSelect.selectedIndex = n;
      break;
    }

  for (n = 0; n < daySelect.length; n++)
    if (daySelect.options[n].value == when.getDate())
    {
      daySelect.selectedIndex = n;
      break;
    }

  for (n = 0; n < hourSelect.length; n++)
    if (hourSelect.options[n].value == when.getHours())
    {
      hourSelect.selectedIndex = n;
      break;
    }

  for (n = 0; n < minuteSelect.length; n++)
    if (minuteSelect.options[n].value == when.getMinutes())
    {
      minuteSelect.selectedIndex = n;
      break;
    }

  syncAmPm(when.getHours());
  syncTimezone(currentTimezone);
};

function syncAmPm(hour)
{
  // Display AM or PM as appropriate for the given hour
  
  var indicator = document.getElementById('am_pm');
  if ((hour == 1) || (hour == 13))
    indicator.innerHTML = '';
  else if (hour < 12)
    indicator.innerHTML = 'AM';
  else
    indicator.innerHTML = 'PM';
};

function syncTimezone(offset)
{
  // Set the on-screen timezone selector to match the given offset (in hours) from GMT
  if (!daylightMap._nightMode)
    for (n = 0; n < tzSelect.length; n++)
      if (tzSelect.options[n].value == offset)
      {
        tzSelect.selectedIndex = n;
        break;
      }
};

function getWhen()
{
  // Return the currently-displayed date/time
  if (daylight.when)
    return daylight.when;
  else
    return daylight.lastRefresh;
};

function getMapOffset()
{
  // Return the timezone offset of the map display, in hours from GMT
  if (daylightMap._nightMode)
    return 0;
  else
    return parseInt(valueOfField(tzSelect)) - getWhen().getTimezoneOffset() / 60;
};

function refreshDaylight()
{
  // Update the daylight plot

  if (refreshing)
    return;
  refreshing = true;
  try
  {
    if (!daylightMap._nightMode)
    {
      if (document.forms['options_content'].elements['current'].checked)
      {
        // Using current date/time
        daylight.when = null;
    
        // Save it to the on-screen selectors
        window.setTimeout('syncDate()', 1000);;
      }
      else
      {
        // Extract date/time from selectors
        var form = document.forms['options_content'];
        currentTimezone = parseInt(valueOfField(tzSelect, form));
        daylight.when = 
          new Date(Date.UTC(2006, 
                            parseInt(valueOfField(monthSelect, form)), 
                            parseInt(valueOfField(daySelect, form)), 
                            parseInt(valueOfField(hourSelect, form)) - currentTimezone,
                            parseInt(valueOfField(minuteSelect, form)),
                            0, 0));
      }
  
      if (points.length == 0)
        sunClick();
    }
    
    daylight.active = true;
  
//  document.getElementById('when').innerHTML = 'As of ' + daylight.when.toDateString() + ', ' + daylight.when, ':') + ':';
  
    daylight.refresh();

    // Refresh all point info to match the new time
    for (var n = 0; n < points.length; n++)
      points[n].refreshInfo();
  
    // Refresh the "Link to this map" URL  
    makeLink();
    
//    // The test cookie is just to see if the browser is supporting persistent cookies
//    document.cookie = 'test=1;expires=' + cookieDate(new Date(Number(new Date()) + _mSecPerDay));
  }
  catch(error)
  {
  }
  refreshing = false;
};

function autoRefresh(state)
{
  // Set the map's auto-refresh to the given boolean state
  
  // Turn any existing auto-refresh OFF
  if (timerId)
    window.clearInterval(timerId);

  if (state && !daylightMap._nightMode)
  {
    // turn auto-refresh ON
    timerId = window.setInterval('refreshDaylight()', _refreshInterval);
    document.forms['options_content'].elements['current'].checked = true;

    // If needed, refresh the display right now also
    var now = new Date();
    if (now - getWhen() > _refreshInterval)
      refreshDaylight();

// I don't think this call is needed? SCU, 4 May 06
//    makeLink();
  }
};

function windowResize()
{
  // Dynamic CSS to make everything fit when the window size changes
  
  winWidth  = windowWidth() - 1;
  winHeight = windowHeight();

  mainDiv.style.width = winWidth + 'px';
  
  if (hideChrome)
  {
    if (titleDiv)
      titleDiv.style.display = 'none';
    optionsDiv.style.display = 'none';
    var mapWidth = winWidth;
  }
  else
  {
    if (titleDiv)
      titleDiv.style.display = 'block';
    optionsDiv.style.display = 'block';
    var mapWidth = (winWidth - (121 + optionsDiv.offsetWidth));
  }

  if (mapWidth)
  {
    mapDiv.style.width = mapWidth + 'px';
    optionsDiv.style.left = mapWidth + 'px';
//    adsDiv.style.width = (mapWidth + 1) + 'px';
    google_ad_width = mapWidth + 'px';
  }

  if (winHeight)
  {
    optionsDiv.style.height = (winHeight - 2) + 'px';

    if (hideChrome)
    {
      adsDiv.style.display    = 'none';
      meridians.style.display = 'none';
      var mapHeight = winHeight;
    }
    else
    {
      adsDiv.style.display    = 'block';
      meridians.style.display = 'block';

      var mapHeight = winHeight - meridians.offsetHeight;
//      adsDiv.style.top = mapHeight + 'px';
//      mapHeight -= meridians.offsetHeight;
      meridians.style.top = mapHeight + 'px';
    }
  
    mapDiv.style.height = mapHeight + 'px';
    adsDiv.style.height = mapHeight + 'px';
  }    

  if (expanded &&
      !hideChrome)
  {
    markerList.style.height = (parseInt(mapHeight) - markerList.offsetTop) + 'px';
  }
};

function toggleOptions()
{
  // Hide/show the Options bar
  
  if (!expanded)
  {
    // Show it
    optionsDiv.style.width = '22em';
    optionsBtn.style.height = '22px';
    optionsBtn.style.backgroundImage = 'url(i18n/' + language + '_options_h.png)';
    optionsBtn.style.backgroundPosition = 'left 0px';
    optionsContent.style.display = 'block';
  }
  else
  {
    // Hide it
    optionsDiv.style.width = '22px';
    optionsBtn.style.height = '100%';
    optionsBtn.style.backgroundImage = 'url(i18n/' + language + '_options_v.png)';
    optionsBtn.style.backgroundPosition = 'left 18px';
    optionsContent.style.display = 'none';
  }

  // Save the new state  
  expanded = !expanded;

  // Adjust other screen elements accordingly
  windowResize();
  map.checkResize();
  showFlags(false);
  refreshMeridians();
};

function showFlags(show)
{
  var optWidth = elementWidth(optionsBtn);
  
  if (show && !flagsShown)
  {
    // Expand to show the full flag list

    flagDiv.style.height = '48px';
    flagDiv.style.width  = '17px';
    flagDiv.style.border = '1px solid black';
    flagDiv.style.paddingTop  = '1px';
    flagDiv.style.paddingLeft = '1px';
    if (expanded)
    {
      flagDiv.style.left = (optWidth - 22) + 'px';
      flagDiv.style.top  = '3px';
    }
    else
    {
      flagDiv.style.left = '1px';
      flagDiv.style.top  = '1px';
    }

    flagsShown = true;
  }
  else
  {
    // Contract to only show the active flag

    flagDiv.style.height = '11px';
    flagDiv.style.width  = '16px';
    flagDiv.style.border = 'none';
    flagDiv.style.paddingTop  = '0px';
    flagDiv.style.paddingLeft = '0px';
    if (expanded)
    {
      flagDiv.style.left = (optWidth - 20) + 'px';
      flagDiv.style.top  = '5px';
    }
    else
    {
      flagDiv.style.left = '3px';
      flagDiv.style.top  = '3px';
    }

    flagsShown = false;
  }
};

function setLanguage(newLanguage)
{
  showFlags(false);
  if (language != newLanguage)
  {
    if (titleDiv)
      var url = linkTo.href;
    else
      var url = window.location;

    if (url.indexOf('hl=') == -1)
    {
      if (url.indexOf('?') == -1)
        url += '?';
      else
        url += '&';
      url += 'hl=' + newLanguage;
    }
    else
      url = url.replace('hl=' + language, 'hl=' + newLanguage);

    window.location = url;
  }
};

function infoWindowOpen()
{
  if (titleDiv)
  {
    // Make sure the infowindow isn't obscured by the title div
    
    var infoDiv  = document.getElementById('info_window');
    
    var overlapX = (getLeft(titleDiv) + titleDiv.offsetWidth - getLeft(infoDiv)) + 20;
    var overlapY = (getTop(titleDiv) + titleDiv.offsetHeight - getTop(infoDiv)) + 25;
  
    if ((overlapX > 0) && (overlapY > 0))
      map.panBy(new GSize(0, overlapY));
  }
};

function mapMove()
{
  // Refresh the AM/PM display
  refreshMeridians();
};

function mapMoveEnd()
{
  // Disallow panning off the maximum latitude that GMap displays
  var center = map.getCenter();
  if (Math.abs(center.lat()) > _maxLat)
    map.setCenter(new GLatLng(_maxLat * sign(center.lat()), center.lng()))

  // Refresh the InfoWindow
  if (selected && !refreshing)
    showPointInfo(selected);

  // Refresh the "Link to this map"
  makeLink();
};

function makeLink()
{
  // Generate and apply the "Link to this map" URL

  var cookie = '';

  // Start with map center point and zoom level
  var center = map.getCenter();
  var parms = '?lat=' + center.lat().toFixed(6)
            + '&lng=' + center.lng().toFixed(6) 
            + '&z=' + map.getZoom();

  // Map type (satellite, hybrid, etc)
  switch (map.getCurrentMapType())            
  {
    case G_SATELLITE_MAP: parms += '&t=s'; break;
    case G_HYBRID_MAP:    parms += '&t=h'; break;
    case G_PHYSICAL_MAP:  parms += '&t=p'; break;
    case G_NORMAL_MAP:    parms += '&t=m'; break;
  }

  if (daylightMap._nightMode)
    parms += '&night=1';
  else
  {
    // Date/time shown  
    var linkDate = getWhen();
    if (document.forms['options_content'].elements['current'].checked ||
        !linkDate)
    {
      parms += '&c=1';
    }
    else
    {
      linkDate = new Date(Number(linkDate) + (linkDate.getTimezoneOffset() + (currentTimezone * 60)) * 60 * 1000);
      parms += '&d=' + encodeURIComponent(formatDate(linkDate) + ' ' + formatTime(linkDate, {seconds: false}))
      parms += '&tz=' + currentTimezone;
    }

    // Sun position    
    if (sunCbx.checked)
    {
      parms += '&s=1';
      if (saveCbx.checked)
        document.cookie = 's=1;expires=' + cookieExpire;
    }
    else
      // delete the cookie
      document.cookie = 's=;expires=Sunday, 24-Apr-05 00:00:00 GMT';
  }

  // Map markers
  
  switch (valueOfField(markerStyle)) 
  {
    case 'analog':  parms += '&m=a'; break;
    case 'digital': parms += '&m=d'; break;
    case 'color':   parms += '&m=c'; break;
  }

  for (var n = 0; n < points.length; n++)
  {
    parms += '&x' + n + '=' + points[n].marker.getPoint().lng().toFixed(6)
           + '&y' + n + '=' + points[n].marker.getPoint().lat().toFixed(6)
           + '&n' + n + '=' + encodeURIComponent(points[n].name);
          

    if (saveCbx.checked)
      cookie += encodeURIComponent(points[n].marker.getPoint().lat().toFixed(6) + '>' +
                                   points[n].marker.getPoint().lng().toFixed(6) + '>' +
                                   points[n].name.htmlEntities() + '>>');
  }

  if (language != '')
    parms += '&hl=' + language

  if (titleDiv)
    // Apply the new URL to the link object
    linkTo.href = documentPath() + parms;
  
  // Also save the points list in a cookie 
  if (saveCbx.checked)
  {
    if (cookie == '')
      // Points list is empty => delete the cookie
      document.cookie = 'points=;expires=Sunday, 24-Apr-05 00:00:00 GMT';
    else
      document.cookie = 'points=' + cookie + ';expires=' + cookieExpire;
  }
};

function showPlacer(method)
{
  // Show the control for placing a new map marker
  switch (valueOfField(method)) 
  {
  	case 'click':
  	{
  	  // This "control" is mostly just instructions
  		var innerHTML = '<p>' + l10n.by_click + '</p>'
  		              + '<p id="cancel"><button onclick="hidePlacer(); return false">' + l10n.cancel + '</button></p>';
      mapDiv.firstChild.firstChild.style.cursor = "default"; 
  		clicking = true;
  		break;
  	}

  	case 'address':
  	{
  		var innerHTML = '<p>' +
  		                  '<span class="addr_label">' + l10n.address + ':</span> ' +
  		                  '<input type="text" name="address" id="address" onkeypress="if (event.keyCode == 13) {placeAddr(); return false}" /> ' +
  		                '</p>' +
  		                '<p>' +
  		                  '<span class="addr_label">' + l10n.city + ':</span> ' +
  		                  '<input type="text" name="city" id="city" onkeypress="if (event.keyCode == 13) {placeAddr(); return false}" /> ' +
  		                '</p>' +
  		                '<p>' +
  		                  '<span id="state_label">' + l10n.state + ':</span> ' +
  		                  '<input type="text" name="state" id="state" maxlength="2" onkeypress="if (event.keyCode == 13) {placeAddr(); return false}" /> ' +
  		                '</p>' +
  		                '<p>' +
  		                  '<span class="addr_label">' + l10n.zip + ':</span> ' +
  		                  '<input type="text" name="zip" id="zip" maxlength="10" onkeypress="if (event.keyCode == 13) {placeAddr(); return false}" /> ' +
  		                  '(' + l10n.us_only + ')' +
  		                '</p>' +
  		                '<p id="cancel">' +
  		                  '<span id="progress"></span>' +
  		                  '<button onclick="placeAddr(); return false">  OK  </button>' +
  		                  '<button onclick="hidePlacer(); return false">' + l10n.cancel + '</button>' +
  		                '</p>';
  		var focusId = 'address';
  		break;
  	}

  	case 'coords':
  	{
  		var innerHTML = '<p>' +
  		                  '<span class="coord_label">' + l10n.lat + ':</span> ' +
  		                  '<input type="text" name="lat" id="lat" onkeypress="if (event.keyCode == 13) {placeLatLon(); return false}" /> ' +
  		                  '<label for="north"><input type="radio" name="hemi_ns" id="north" value="1" />' + l10n.n + '</label> ' +
  		                  '<label for="south"><input type="radio" name="hemi_ns" id="south" value="-1" />' + l10n.s + '</label> ' +
  		                '</p>' +
  		                '<p>' +
  		                  '<span class="coord_label">' + l10n.lon + ':</span> ' +
  		                  '<input type="text" name="lon" id="lon" onkeypress="if (event.keyCode == 13) {placeLatLon(); return false}" /> ' +
  		                  '<label for="east"><input type="radio" name="hemi_ew" id="east" value="1" />' + l10n.e + '</label> ' +
  		                  '<label for="west"><input type="radio" name="hemi_ew" id="west" value="-1" />' + l10n.w + '</label> ' +
  		                '</p>' +
  		                '<p id="cancel">' +
  		                  '<button onclick="placeLatLon(); return false">  OK  </button>' +
  		                  '<button onclick="hidePlacer(); return false">' + l10n.cancel + '</button>' +
  		                '</p>';
  		var focusId = 'lat';
  		break;
  	}
  }

  if (innerHTML)
  {
    // Display the control's HTML  
    placer[1].innerHTML = innerHTML;
    placer[0].style.display = 'block';

    // Set focus to the control
    if (focusId &&
        (focusControl = document.getElementById(focusId)))
      focusControl.focus();
  }
};

function hidePlacer()
{
  // Hide the control used for placing a new map marker

  selector.selectedIndex = 0;
  placer[0].style.display = 'none';
  
  clicking = false;
}

function mapClick(overlay, coords)
{
  if (coords && clicking)
    // Map is in "Place marker by click" mode
    placeMarker(coords)
};

function placeAddr()
{
  // Create a new map marker by geocoding the suppled address
  
  var street = valueByName('address', document.forms['options_content']);
  var city   = valueByName('city',    document.forms['options_content']);
  var state  = valueByName('state',   document.forms['options_content']);
  var zip    = valueByName('zip',     document.forms['options_content']);

  if (!street && !city && !state && !zip)
  {
    alert(l10n.addr_msg_1);
    return;
  }

  // Display a progress message
  var progress = document.getElementById('progress');
  if (progress)
    progress.innerHTML = 'Searching...'; 
 
  // Build the geocode URL
  
  var infoURL = '/geocode.php?';
  
  if (street)
    infoURL += 'street=' + street + '&';
  if (city)
    infoURL += 'city=' + city + '&';
  if (state)
    infoURL += 'state=' + state + '&';
  if (zip)
    infoURL += 'zip=' + zip + '&';

  // Fetch the URL
  request.open('GET', infoURL, true);
  request.onreadystatechange = function()
  {
    if (request.readyState == 4)
    {
      try
      {
        var xmlDoc = request.responseXML;
  
        // Hide the progress message
        var progress = document.getElementById('progress');
        if (progress)
          progress.innerHTML = ''; 
        
        if (message = getText(xmlDoc.documentElement.getElementsByTagName('Message')))
        {
          alert(l10n.addr_msg_2 + ': ' + message);
        }
        else
        {
          // Looks good so far
          
          var lat = parseFloat(getText(xmlDoc.documentElement.getElementsByTagName('Latitude')));
          var lon = parseFloat(getText(xmlDoc.documentElement.getElementsByTagName('Longitude')));

          if (isNaN(lat) || isNaN(lon))
          {
            // Something wrong with the lat/lon returner
            hidePlacer();
            return;
          }
          
          // Try to generate a reasonable name
          var street = getText(xmlDoc.documentElement.getElementsByTagName('Address'));
          if (street)
            var name = street;
          else
          {
            var city   = getText(xmlDoc.documentElement.getElementsByTagName('City'));
            var state  = getText(xmlDoc.documentElement.getElementsByTagName('State'));
            
            if (city)
              var name = city + ', ' + state;
            else
              var name = state;
          }
          
          // Create the map marker
          placeMarker(new GLatLng(lat, lon), name);
        }
      }
      catch(error)
      {
//        GLog.write('Geocode error');
      }
    }
  };
  request.send(null);
};

function placeLatLon()
{
  // Create a new map marker at the user-entered lat/lon coords
  
  var lat = valueByName('lat', document.forms['options_content']);
  var lon = valueByName('lon', document.forms['options_content']);
  if (!lat || !lon)
  {
    alert(l10n.no_latlon_msg);
    return;
  }

  lat = parseFloat(lat);
  lon = parseFloat(lon);
  if (isNaN(lat) || isNaN(lon))
  {
    alert(l10n.bad_latlon_msg);
    return;
  }

  var hemiEW = valueByName('hemi_ew', document.forms['options_content']);
  var hemiNS = valueByName('hemi_ns', document.forms['options_content']);
  if (!hemiEW || !hemiNS)
  {
    alert(l10n.no_hemi_msg);
    return;
  }
  
  hemiEW = parseInt(hemiEW);
  hemiNS = parseInt(hemiNS);
  
  placeMarker(new GLatLng(lat * hemiNS, lon * hemiEW));
};

function placeMarker(coords, name)
{
  // Create a new Point-of-Interest at the given lat/lon coords, and (optionally) with the given name

  hidePlacer();
  
  if (points.length < _maxPoints)
  {
    if (!name)
      // Name not supplied - generate one from the point ID
      name = l10n.marker + ' ' + String(nextId + 1);

    // Create the point
    points.push(new pointOfInterest(name, coords));

    // Make sure the new point is within the visible map view
    if (!map.getBounds().contains(coords))
      map.panTo(coords);

    // If the map's InfoWindow is open, move it to the new point
    if (selected)
      points[points.length - 1].showInfo();
  }
  else
    alert(l10n.max_msg);
};

function getIcon()
{
  var iconStyle = valueOfField(markerStyle);
  if (iconStyle != 'color')
    return clockIcons[iconStyle];
  else
  {
    // Return a uniquely-colored map pointer icon
  
    var m;
    var found;
    
    for (var n = 0; n < icons.length; n++)
    {
      if (!icons[n])
      {
        // Create a new icon
        icons[n] = new GIcon(G_DEFAULT_ICON);
        icons[n].image            = '/images/20_' + _iconColors[n] + '.png';
        icons[n].shadow           = '/images/20_shadow.png';
        icons[n].iconSize         = new GSize(12, 20);
        icons[n].shadowSize       = new GSize(22, 20);
        icons[n].iconAnchor       = new GPoint(6, 20);
        icons[n].infoWindowAnchor = new GPoint(5, 1);
        icons[n].printImage       = '/images/20_' + _iconColors[n] + '.gif';
        icons[n].mozPrintImage    = '/images/20_' + _iconColors[n] + '.gif';
        icons[n].printShadow      = '/images/20_shadow.gif';
        break;
      }
      else
      {
        // Is this icon available for re-use? 
        
        found = false;
        for (m = 0; m < points.length; m++)
          if (points[m].marker.getIcon() == icons[n])
          {
            // Icon is in use by this point
            found = true;
            break;
          }
          
        if (found)
          // Icon not available
          continue;
        else
          // Icon is available
          break;
      }
    }
    
    return icons[n];
  }
};

function selectList(id)
{
  // Display the given point as selected on the list in the option bar

  var listItem = document.getElementById('list_' + id);
  if (listItem)
  {
    // Set the background color and mouse pointer
    listItem.style.background = '#FFFFA0';
    listItem.style.cursor = 'default';

    // Set the name field up for "click to rename"
    var listName = document.getElementById('name_' + id);
    if (listName)
    {
      listName.style.cursor = 'text';
      listName.onclick = function() {renamePoint(id, 'list')};
    }

    // Scroll to make the selected point visible, as necessary
    
    if (listItem.offsetTop - markerList.offsetTop < markerList.scrollTop)
      // Selected item is off the top of the visible list - scroll up to reveal it
      markerList.scrollTop = listItem.offsetTop - markerList.offsetTop;

    else if ((listItem.offsetTop - markerList.offsetTop + listItem.offsetHeight) > 
             (markerList.scrollTop + markerList.offsetHeight))
      // Selected item is off the bottom of the visible list - scroll down to reveal it
      markerList.scrollTop = (listItem.offsetTop + listItem.offsetHeight) - 
                             (markerList.offsetTop + markerList.offsetHeight);
  }
  
  selected = id;
};

function unSelectList()
{
  // De-select any markers from the list in the option bar
  if (selected != null)
  {
    var listItem = document.getElementById('list_' + points[selected].id);
    if (listItem)
    {
      // Set the background color and mouse pointer
      listItem.style.background = '';
      listItem.style.cursor = 'pointer';

      // Discontinue "click to rename"
      var listName = document.getElementById('name_' + points[selected].id);
      if (listName)
      {
        listName.style.cursor = '';
        listName.onclick = function() {};
      }
    }
      
    selected = null;
  }
};

function displayOffset(hours)
{
  // Format a timezone offset (supplied in hours) for display, such as '+0430' for 4.5 hours
  
  var result = '';
  
  if (hours == Math.abs(hours))
    result += '+';
  else
    result += '-';
    
  hours = Math.abs(hours);
  var minutes = (hours - Math.floor(hours)) * 60;
  var intHours = Math.floor(hours);
  
  result += zeroPad(intHours, 2) + zeroPad(minutes, 2);
  
  return result;
}

function savePoints(state)
{
  // Either set or clear a persistent cookie containing marker info, based on the boolean parm
  
  if (state)
  {
    if (document.cookie.search('cookies_enabled=1') == -1)
    {
      // Browser appears to have persistent cookies disabled
      alert(l10n.no_cookie_msg) 
      saveCbx.checked = false;
      return false;
    }
  
    // Calling the makeLink function will update the points cookie
    makeLink();

    // Delete the "don't save" cookie
    document.cookie = 'noSave=1;expires=Sun, 24-Apr-05 00:00:00 GMT';
  }
  else
  {
    // Delete the points and sun cookies
    document.cookie = 'points=;expires=Sun, 24-Apr-05 00:00:00 GMT';
    document.cookie = 's=;expires=Sun, 24-Apr-05 00:00:00 GMT';

    // Add an additional cookie to keep the checkbox turned off
    document.cookie = 'noSave=1;expires=' + cookieDate(new Date(Number(new Date()) + 365 * _mSecPerDay));
  }
};

function movePoint(id)
{
  // Prepare a map marker for drag+drop

  var point = getPoint(id);
  if (point)
    try
    {
      map.closeInfoWindow();
  
      if (!shownInstructions)
      {
        shownInstructions = true;
        alert(l10n.move_msg);
      }

//      point.marker.disableDragging();
      point.marker.enableDragging();
    }
    catch(e)
    {
//      GLog.write('Marker drag error: ' + e.toString());
    }
};

function markerMoveEnd(id)
{
  // Finalize map marker drag+drop

  var point = getPoint(id);
  if (point)
  {
    point.marker.disableDragging();
    point.coords = point.marker.getPoint();
    point.refreshInfo();
  }
  
  makeLink();
};

function deletePoint(id, confirmFirst)
{
  // Remove a marker from the map and all associated lists
  
  if (confirmFirst == null)
    confirmFirst = true;
  
  var point = getPoint(id);
  if (point)
    if (!confirmFirst ||
        confirm(l10n.delete_msg + ' "' + point.name + '"?'))
    {
      // Remove it from the map
      map.closeInfoWindow();
      map.removeOverlay(point.marker);

      // Remove it from the option-bar list
      var listItem = document.getElementById('list_' + id);
      if (listItem)
        listItem.parentNode.removeChild(listItem);

      // Remove it from the internal Point-of-Interest list
      points.splice(points.indexOf(point), 1);
      
      makeLink();
    }
};

function deleteAll()
{
  for (var n = (points.length - 1); n >= 0; n--)
    deletePoint(points[n].id, false);
  
  nextId = 0;
};

function renamePoint(id, where)
{
  // Initialize map marker renaming
  
  var point = getPoint(id);
  
  if (where == 'list')
    // Renaming in the option-bar list
    var nameElement = document.getElementById('name_' + id);
  else
    // Renaming in the infowindow
    var nameElement = document.getElementById('info_name_' + id);
    
  if (point && nameElement)
  {
    // Replace the displayed name with a text input in the HTML
    nameElement.onclick = null;
    nameElement.innerHTML = '<input type="text" id="renamer" value="' + point.name + '" ' + 
                            'maxlength="25" onblur="applyNewName(' + id + ', this.value)" ' +
                            'onkeypress="if (event.keyCode == 13) applyNewName(' + id + ', this.value)" />';
  
    // Set focus to the text input and select its contents
    var renamer = document.getElementById('renamer');
    if (renamer)
    {
      renamer.select();
      renamer.focus();
    }
  }
};

function applyNewName(id, value)
{
  // Finalize map marker renaming
  if (value != '')
  {
    var point = getPoint(id);
  
    if (point)
    {
      // Rename the internal point object
      point.name = value;
  
      // Apply the new name to both places it may occur in the HTML
  
      var nameElement = document.getElementById('name_' + id);
      if (nameElement)
      {
        nameElement.onclick = function() {renamePoint(id, 'list')};
        nameElement.innerHTML = point.name;
      }
  
      nameElement = document.getElementById('info_name_' + id);
      if (nameElement)
      {
        nameElement.onclick = function() {renamePoint(id, 'info')};
        nameElement.innerHTML = point.name;
      }
    }
    
    makeLink();
  }
};

function toggleChrome()
{
  if (hideChrome)
  {
    map.addControl(panControl, new GControlPosition(G_ANCHOR_TOP_LEFT, new GSize(10, 85)));
    map.addControl(typeControl);
    map.addControl(scaleControl);
  }
  else
  {
    map.removeControl(panControl);
    map.removeControl(typeControl);
    map.removeControl(scaleControl);
  }
  
  hideChrome = !hideChrome;
  windowResize();
};

function sunMove(xml)
{
  if (xml)
  {
    sunCoords = new GLatLng(parseFloat(getText(xml.documentElement.getElementsByTagName('solar_lat'))),
                            parseFloat(getText(xml.documentElement.getElementsByTagName('solar_lon'))));

    // Refresh the AM/PM display
    refreshMeridians();

    if (sunCbx.checked)
    {
      if (!sunOnMap)
      {
        map.addOverlay(sunMarker);
        sunOnMap = true;
      }
  
      sunMarker.setPoint(sunCoords);
      
      var display = document.getElementById('sun_coords');
      display.innerHTML = '(' + numberFormat(sunCoords.lat(), 6) + ', ' + numberFormat(sunCoords.lng(), 2) + ')';
    }
  }
};

function sunClick()
{
  try
  {
    var infoURL = '/point_info.php?dtmWhen=' + Number(getWhen()) / 1000;
  
    request.open('GET', infoURL, true);
    request.onreadystatechange = function()
    {
      if (request.readyState == 4)
        sunMove(request.responseXML);
    };
    request.send(null);

    if (!sunCbx.checked &&
        sunMarker && 
        sunOnMap)
    {
      map.removeOverlay(sunMarker);
      sunOnMap = false;
    }
  }
  catch (e)
  {
    // not much we can do here
  }
};

function keyHandler(event)
{
  // Handle map shortcut keys

  // Cross-browser compatible code to retrieve the key code
  if (!event)
    event = window.event;     // IE
  var code = event.which;     // FF
  if (code == undefined)
    var code = event.keyCode; //IE

  if (event.ctrlKey || 
      event.altKey ||
      expanded)
    return;

  switch (String.fromCharCode(code))
  {
    case '+':
    {
      // Zoom in
      map.setZoom(map.getZoom() + 1);
      break
    }

    case '-':
    {
      // Zoom out
      map.setZoom(map.getZoom() - 1);
      break
    }

    case 'f':
    case 'F':
    {
      // Toggle "full screen" mode
      toggleChrome();
    }
  }
};

function refreshMeridians()
{
  if (!sunCoords)
    return;

  var html = '';
  var className;
  var left = new GPoint(0, 0);
  var right;
  var width;
  var coords;
  var wrap = map.getCurrentMapType().getProjection().getWrapWidth(map.getZoom());

  do {
    coords = map.fromContainerPixelToLatLng(left);

    if (coords.directionFrom(sunCoords) >= 0)
    {
      className = 'am';
      right = map.fromLatLngToContainerPixel(sunCoords);
    }
    else
    {
      className = 'pm';
      right = map.fromLatLngToContainerPixel(new GLatLng(0, sunCoords.lng() + 180));
    }

    while (right.x < left.x)
      right.x += wrap;
    while (right.x - left.x > wrap)
      right.x -= wrap;

    width = Math.min(right.x, mapDiv.offsetWidth) - left.x;

    html += '<span class="' + className + '"' +
            ' style="left: ' + left.x + 'px; width: ' + width +'px">';
    if (width > 25)
      html += className.toUpperCase();
    html += '</span>';

    left = new GPoint(right.x + 1, 0);
  } while (right.x <= mapDiv.offsetWidth)
  
  meridians.innerHTML = html;
};

function updateStyle()
{
  for (var n = 0; n < points.length; n++)
  {
    var listImage = points[n].setIcon();
    var listTime = document.getElementById('icon_' + n);
    points[n].refreshIcon();
  }
};

function pointMouseOut(id)
{
  if (tooltip)
    tooltip.hide();
};

function pointMouseOver(id)
{
  var point = points[id];

  if (point &&
      (selected != id))
  {
    var html = '<p>' + point.name + ': ' + point.displayTime(getWhen()) + '<p>';

    if (tooltip)
    {
      tooltip.setContents(html);
      tooltip.setPoint(point.coords);
      tooltip.show();
    }
    else
    {
      tooltip = new ELabel(point.coords, html, 'tooltip', new GSize(16, -9), 85, true);
      map.addOverlay(tooltip);
    }
  }
};




