// ------------------------------------------
// Global variables
// ------------------------------------------
// City list
var cityList  = new Array();
// Stop list
var stopList  = new Array();
// Route stops list
var crteList  = new Array();
var srteList  = new Array();
// Print icon URL
var iconPrint = null;

// Focus tracking
var bloaded  = false;
var focusDep = false;
var focusArr = false;
var focusLst = false;
var showLst  = false;

// Google maps
var googleMap = null;
var geocoder  = null;
var iconMap   = null;
var maxe      = -200;
var maxn      = -200;
var maxw      = +200;
var maxs      = +200;

// ------------------------------------------
// Set mailto target dynamically
// ------------------------------------------
// nanika.net
function setTarget(obj, nom)
{
  dest  = "mai";
  dest += "lto";
  dest += "\x3A";
  dest += nom;
  dest += "\x40";
  dest += "nan";
  dest += "ika";
  dest += "\x2e";
  dest += "\x6E\x65\x74";

  obj.href = dest;

  return true;
}

// yahoo.com
function setTarget2(obj, nom)
{
  dest  = "mai";
  dest += "lto";
  dest += "\x3A";
  dest += nom;
  dest += "\x40";
  dest += "ya";
  dest += "hoo";
  dest += "\x2e";
  dest += "\x63\x6F\x6D";

  obj.href = dest;

  return true;
}

// ------------------------------------------
// Add a bookmark
// ------------------------------------------
function addBookmark(title, url)
{
  /* Not working in Opera 9!
  if (window.opera && window.print) // opera
  {
    var elem = document.createElement('a');
    elem.setAttribute('href', url);
    elem.setAttribute('title', title);
    elem.setAttribute('rel', 'sidebar');
    elem.click();
  }
  else if (document.all) // ie */
  if (!window.opera && document.all)
  {
    window.external.AddFavorite(url, title);
  }
  // if (window.sidebar) // firefox
  else
  {
    // window.sidebar.addPanel(title, url, '');
    alert('Right-click on the link & select "Add bookmark"');
  }

  return false;
}

// ------------------------------------------
// Test for iPhone or similar
// ------------------------------------------
function iPhone()
{
  // Get user agent
  var ua = navigator.userAgent.toLowerCase();

  // Check a number of fields
  if (ua.indexOf(' applewebkit/') > 0
   && ua.indexOf(' mobile/') > 0
   && ua.indexOf('mozilla/5.0') >= 0)
  {
    return true;
  }

  return false;
}

// ------------------------------------------
// Actions on page load
// ------------------------------------------
function onLoadPage()
{
  // On city selection form?
  var form = document.getElementById('formcity');
  if (form)
  {
    // Reset position in lists
    if (form.r)
    {
	    form.r.selectedIndex = 0;
	}
    form.c.selectedIndex = 0;
  }

  // On results page
  form = document.getElementById('backroute');
  if (form)
  {
    // Add print button
    var divprint = document.getElementById('printicon');
    if (divprint)
    {
      insertPrint(divprint);
    }
    
    // Prepare route lists (for maps)
    var spanrte = document.getElementById('showrte1');
    if (spanrte && GBrowserIsCompatible())
    {
      // Fill lists
      initRouteList();
    }
  }

  // Set focus on first item
  setDefaultFocus();

  // Load complete
  // bloaded = true;
  setTimeout(function() { bloaded = true; }, 0);
}

// ------------------------------------------
// Actions on page unload
// ------------------------------------------
function onUnloadPage()
{
  // Reload necessary
  bloaded = false;
  // Google Maps clean-up
  GUnload();
}

// ------------------------------------------
// Set focus on the given item
// ------------------------------------------
function setFocus(item)
{
  // Get item to focus on
  var elem = document.getElementById(item);

  // Check one is present
  if (elem)
  {
    // Set focus
    elem.focus();
  }
}

// ------------------------------------------
// Set focus on first item if possible
// ------------------------------------------
function setDefaultFocus()
{
  // Focus on "first" item
  setFocus('first');
}

// ------------------------------------------
// License pop-up
// ------------------------------------------
function showLicense(url)
{
  window.open(url,"default","width=640,height=480,resizable,scrollbars");
}

// ------------------------------------------
// Insert a print command
// ------------------------------------------
function insertPrint(divprint)
{
  // Only if "print()" available
  if (iconPrint && window.print)
  {
    var codeprint = '<a href="javascript:window.print()">';
    codeprint    += '<img src="' + iconPrint;
    codeprint    += '" border="0" alt="Print" title="Print"></a>';
    divprint.innerHTML = codeprint;
  }
}

//------------------------------------------
// Region/country/city management
//------------------------------------------
// Constructor for a simple city object
// ------------------------------------------
function city(region, country, city, code)
{
  this.region  = region;
  this.country = country;
  this.city    = city;
  this.code    = code;
}

// ------------------------------------------
// City sorting functions
// ------------------------------------------
function sortListCountry(a, b)
{
  if (a.country > b.country)
  {
    return 1;
  }
  else
  {
    return -1;
  }
}
function sortListCity(a, b)
{
  if (a.city > b.city)
  {
    return 1;
  }
  else
  {
    return -1;
  }
}

// ------------------------------------------
// React to a region selection
// ------------------------------------------
function selectRegion()
{
  // Adjust country list
  fillCountryList();
  // Adjust city list
  fillCityList();
  return true;
}

// ------------------------------------------
// React to a country selection
// ------------------------------------------
function selectCountry()
{
  // Adjust city list
  fillCityList();
  return true;
}

// ------------------------------------------
// Fill country list with current criteria
// ------------------------------------------
function fillCountryList()
{
  var i, j;

  // Get objects
  var form          = document.getElementById('formcity');
  var regionSelect  = form.r;
  var region        = "";
  // Region selection is present
  if (regionSelect)
  {
    region          = regionSelect.options[regionSelect.selectedIndex].value;
  }
  // Region is pre-selected
  else
  {
    // Get filter
    region          = document.getElementById('filter').value;
  }
  var countrySelect = form.c;
  var country       = "";
  var result        = new Array();

  // Loop on cities to find the countries for that region
  for (i=0, j=0; i<cityList.length; i++)
  {
    // Matching country
    if ((cityList[i].region == region || region == "") && cityList[i].country != country)
    {
      result[j++] = cityList[i];
      country     = cityList[i].country;
    }
  }

  // Sort the list
  result.sort(sortListCountry);

  // Reset country selection list
  countrySelect.options.length = result.length + 1;

  // Fill display list
  for (i=0; i<j; i++)
  {
    countrySelect.options[i+1].text  = result[i].country;
    countrySelect.options[i+1].value = result[i].country;
  }

  // Set default selection
  // Opera bug #226934 (23/10/07)
  // countrySelect.selectedIndex = 0;
  setTimeout(function() { countrySelect.selectedIndex = 0; }, 0);
}

// ------------------------------------------
// Fill city list with current criteria
// ------------------------------------------
function fillCityList()
{
  var i, j;

  // Get objects
  var form          = document.getElementById('formcity');
  var regionSelect  = form.r;
  var region        = "";
  // Region selection is present
  if (regionSelect)
  {
    region          = regionSelect.options[regionSelect.selectedIndex].value;
  }
  // Region is pre-selected
  else
  {
    // Get filter
    region          = document.getElementById('filter').value;
  }
  var countrySelect = form.c;
  var country       = countrySelect.options[countrySelect.selectedIndex].value;
  var citySelect    = form.v;
  var result        = new Array();

  // Loop on cities to find the cities for that country
  for (i=0, j=0; i<cityList.length; i++)
  {
    if ((cityList[i].country == country || country == "")
     && (cityList[i].region  == region  || region  == ""))
    {
      result[j++] = cityList[i];
    }
  }

  // Sort the list
  result.sort(sortListCity);

  // Reset city selection list
  citySelect.options.length = result.length;

  // Fill display list
  for (i=0; i<j; i++)
  {
    citySelect.options[i].text  = result[i].city;
    citySelect.options[i].value = result[i].code;
  }

  // Set default selection
  // Opera bug #226934 (23/10/07)
  // citySelect.selectedIndex = 0;
  setTimeout(function() { citySelect.selectedIndex = 0; }, 0);
}

// ------------------------------------------
// Focus management for assisted field
// ------------------------------------------
// Set or remove focus on a field
// ------------------------------------------
function lateReleaseDepFocus()
{
  // Deferred on field onblur() for iPhone/Opera bug (23/10/07)
  setTimeout(function() { setDepFocus(false, null, null); }, 0);
}
function setDepFocus(b, msgdep, msgadr)
{
  // Bug in IE: wait for complete initialization
  if (!bloaded)
  {
    return;
  }

  // Save state
  focusDep = b;

  // Change select layer visibility
  if (b)
  {
    // Fill list
    fillStopList(true);
    // Set help message
    var help = document.getElementById('helptxt');
    var chad = document.getElementById('adrdep');
    if (help && chad)
    {
      help.innerHTML = (chad.checked ? msgadr : msgdep);
    }
  }
  else if (!focusLst)
  {
    // Get form
    // var form = document.getElementById('formcalc');
    // Reset selection
    // form.dd.selectedIndex = -1;
    // Get list and reset selection
    var list = document.getElementById('dd');
    list.selectedIndex = -1;
    // Hide element
    hideLayer('seldep');
    showLst = false;
  }
}
function lateReleaseArrFocus()
{
  // Deferred on field onblur() for iPhone/Opera bug (23/10/07)
  setTimeout(function() { setArrFocus(false, null, null); }, 0);
}
function setArrFocus(b, msgarr, msgadr)
{
  // Bug in IE: wait for complete initialization
  if (!bloaded)
  {
    return;
  }

  // Save state
  focusArr = b;

  // Change select layer visibility
  if (b)
  {
    // Fill list
    fillStopList(false);
    // Set help message
    var help = document.getElementById('helptxt');
    var chad = document.getElementById('adrarr');
    if (help && chad)
    {
      help.innerHTML = (chad.checked ? msgadr : msgarr);
    }
  }
  else if (!focusLst)
  {
    // Get form
    // var form = document.getElementById('formcalc');
    // Reset selection
    // form.zz.selectedIndex = -1;
    // Get list and reset selection
    var list = document.getElementById('zz');
    list.selectedIndex = -1;
    // Hide element
    hideLayer('selarr');
    showLst = false;
  }
}

// ------------------------------------------
// Set or remove focus on a list
// (just keep the information for future use)
// ------------------------------------------
function setLstFocus(b)
{
  focusLst = b;
}

// ------------------------------------------
// Layer management (hide/show)
// ------------------------------------------
// Show a named layer
// ------------------------------------------
function showLayer(layer)
{
  if (document.layers) // NS 4
  {
    document.layers[layer].visibility = "show";
    document.layers[layer].display = "block";
  }
  else if (document.all) // IE
  {
    document.all[layer].style.visibility = "visible";
    document.all[layer].style.display = "block";
  }
  else
  {
    document.getElementById(layer).style.visibility = "visible";
    document.getElementById(layer).style.display = "block";
  }
}

// ------------------------------------------
// Hide a named layer
// ------------------------------------------
function hideLayer(layer)
{
  // Empty stop lists before hiding (Opera bug) (23/10/07)
  if (layer == 'seldep')
  {
    var list = document.getElementById('dd');
    if (list)
    {
      list.options.length = 0;
    }
  }
  if (layer == 'selarr')
  {
    var list = document.getElementById('zz');
    if (list)
    {
      list.options.length = 0;
    }
  }

  // Hide element
  if (document.layers)
  {
    document.layers[layer].visibility = "hide";
    document.layers[layer].display = "none";
  }
  else if (document.all)
  {
    document.all[layer].style.visibility = "hidden";
    document.all[layer].style.display = "none";
  }
  else
  {
    document.getElementById(layer).style.visibility = "hidden";
    document.getElementById(layer).style.display = "none";
  }
}

// ------------------------------------------
// Stop selection management
// ------------------------------------------
// Constructor for a simple stop object
// ------------------------------------------
function stop(stop, code)
{
  var rexp = new RegExp("´", "g");

  // Restore standard quotes in name
  this.stop = stop.replace(rexp, "\'");
  this.code = code;
}

// ------------------------------------------
// Show one of the stop lists
// ------------------------------------------
function showStopList(depart)
{
  // If not already displayed
  if (!showLst)
  {
    showLayer(depart ? 'seldep' : 'selarr');
    showLst = true;
  }
}

// ------------------------------------------
// Hide all stop lists
// ------------------------------------------
function hideStopList()
{
  // Hide list if present
  focusLst = false;
  if (showLst)
  {
    // Hide lists
    setDepFocus(false, null, null);
    setArrFocus(false, null, null);
  }
}

// ------------------------------------------
// Fill list with filtered stops
// ------------------------------------------
function fillStopList(depart)
{
  var i;
  // var nstop  = 0;
  var field  = null;
  var list   = null;
  var chadr  = null;
  var text;
  var result = new Array();

  // Departure selection
  if (depart)
  {
    field = document.getElementById('first');
    list  = document.getElementById('dd');
    chadr = document.getElementById('adrdep');
  }
  // Arrival selection
  else
  {
    field = document.getElementById('ze');
    list  = document.getElementById('zz');
    chadr = document.getElementById('adrarr');
  }
  // Get current input
  text = field.value.toUpperCase();

  // Don't process under 2 chars of for an address
  if (text.length < 2 || chadr && chadr.checked)
  {
    // Hide list if present
    hideStopList();
    return;
  }

  // Loop on stops to find all matches
  // for (i=0; i<stopList.length; i++)
  // {
  //   // Got something
  //   if (stopList[i].stop.toUpperCase().indexOf(text) >= 0)
  //   {
  //     // Save all results
  //     result[nstop++] = stopList[i];
  //   }
  // }
  var nstop = getSmartStopList(text, result);

  // Reset city selection list
  list.options.length = nstop;

  // Fill display list
  for (i=0; i<nstop; i++)
  {
    list.options[i].text  = result[i].stop;
    list.options[i].value = result[i].code;
  }

  // Set default selection
  // Opera bug #226934 (23/10/07)
  // list.selectedIndex = -1;
  setTimeout(function() { list.selectedIndex = -1; }, 0);

  // Show list if filled
  // Deferred because of iPhone workaround on onblur()
  setTimeout(function() { showStopList(depart); }, 0);
}

// ------------------------------------------
// Get actual matching stop list (smarter version)
// ------------------------------------------
function getSmartStopList(text, result)
{
  var nstop = 0;
  var i;

  // Convert all accented chars
  text = text.replace(/[ÀÁÂÃÄÅ]/g, 'A');
  text = text.replace(/[ÈÉÊË]/g, 'E');
  text = text.replace(/[ÌÍÎÏ]/g, 'I');
  text = text.replace(/[ÒÓÔÕÖØ]/g, 'O');
  text = text.replace(/[ÙÚÛÜ]/g, 'U');
  text = text.replace(/[ÝŸ]/g, 'Y');
  text = text.replace(/Æ/g, 'AE');
  text = text.replace(/Œ/g, 'OE');
  text = text.replace(/Ñ/g, 'N');
  text = text.replace(/Ç/g, 'C');
  text = text.replace(/Ð/g, 'D');
  text = text.replace(/×/g, 'X');
  text = text.replace(/Š/g, 'S');
  text = text.replace(/Ž/g, 'Z');

  // Remove "invalid" chars
  text = text.replace(/[^A-Z0-9]/g, '');

  // Loop on stops to find all matches
  for (i=0; i<stopList.length; i++)
  {
    // Got something
    if (stopList[i].code.indexOf(text) >= 0)
    {
      // Save all results
      result[nstop++] = stopList[i];
    }
  }

  // Return number of matches
  return nstop;  
}

// ------------------------------------------
// Helper: select a text range
// ------------------------------------------
function selectRange(field, start, end)
{
  if (field.createTextRange)
  {
    var range = field.createTextRange();
    range.moveStart('character', start);
    range.moveEnd('character', end);
    range.select();
  }
  else if (field.setSelectionRange)
  {
    field.setSelectionRange(start, end);
  }
}

// ------------------------------------------
// Process enter key
// ------------------------------------------
function processEnter(evt, depart)
{
  // Get key code
  var key = evt.keyCode;
  if (!key)
  {
    key = evt.which;
  }

  // Process enter key
  if (key == 13)
  {
    // Go to next item
    setFocus(depart ? 'ze' : 'submit');
    return false;
  }
  // Ignore up & down arrow (processed on key down)
  if (showLst && (key == 38 || key == 40))
  {
    return false;
  }
  return true;
}

// ------------------------------------------
// Text change in stop field
// ------------------------------------------
function stopChange(evt, depart)
{
  // Get key code
  var key = evt.keyCode;
  if (!key)
  {
    key = evt.which;
  }

  // Not for an 'action' key
  if (key != 38 && key != 40 && key != 13 && key != 9)
  {
    // Clear any error
    clearErrAddress(depart);

    // Departure selection
    /* if (depart)
    {
      field = document.getElementById('first');
      chadr = document.getElementById('adrdep');
    }
    // Arrival selection
    else
    {
      field = document.getElementById('ze');
      chadr = document.getElementById('adrarr');
    }
    // Get current input
    text = field.value;

    // Special processing for '@'
    if (chadr && text == '@')
    {
      // Not set as an address
      if (!chadr.checked)
      {
        // Force as an address
        chadr.checked = true;
        field.value = "";
        return false;
      }
      // Already as an address
      else
      {
        // Get location
        runLoki(field);
      }
    } */

    // iPhone bug workaround
    if (iPhone())
    {
      // Deferred list update
      setTimeout(function() { fillStopList(depart); }, 0);
    }
    else
    {
      // Update list
      fillStopList(depart);
    }
  }

  return true;
}

// ------------------------------------------
// Route field 'action' events to list
// ------------------------------------------
function manageList(evt, depart)
{
  var list = null;
  var selec;

  // Get key code
  var key = evt.keyCode;
  if (!key)
  {
    key = evt.which;
  }

  // Only if list is displayed - Process down and up arrows
  if (showLst && (key == 38 || key == 40))
  {
    // Departure selection
    if (depart)
    {
      list = document.getElementById('dd');
    }
    // Arrival selection
    else
    {
      list = document.getElementById('zz');
    }

    // Change index
    select = list.selectedIndex + (key == 38 ? - 1 : 1);
    // Check limits
    if (select >= 0 && select<list.options.length)
    {
      list.selectedIndex = select;
    }

    // Set field content
    selectStop(depart);
    return false;
  }
  return true;
}

// ------------------------------------------
// Process a stop selection
// ------------------------------------------
function selectStop(depart)
{
  var field = null;
  var list  = null;
  var stext;

  // Departure selection
  if (depart)
  {
    field = document.getElementById('first');
    list  = document.getElementById('dd');
  }
  // Arrival selection
  else
  {
    field = document.getElementById('ze');
    list  = document.getElementById('zz');
  }

  // Selection
  if (list.selectedIndex >= 0)
  {
    // Get current selection
    stext = list.options[list.selectedIndex].text;

    // Set field text
    field.value = stext;

    // Actual selection
    return true;
  }
  // Ghost selection (Opera) (23/10/07)
  return false;
}

// ------------------------------------------
// Process text entry for a stop
// ------------------------------------------
function enterStop(depart)
{
  // Set selection in field
  if (selectStop(depart))
  {
    // Move focus on next item
    setFocus(depart ? 'ze' : 'submit');
  }
  else
  {
    // Reset focus on current item for Opera (23/10/07)
    setFocus(depart ? 'first' : 'ze');
  }
}

// ------------------------------------------
// Copy time selection from 1 form to the other
// ------------------------------------------
function setTimeSelection()
{
  // Get form
  var form  = document.getElementById('formcalc');
  var form2 = document.getElementById('formline');

  var jsel = form.j;
  var hsel = form.h;
  form2.j.value = jsel.options[jsel.selectedIndex].value;
  form2.h.value = hsel.options[hsel.selectedIndex].value;
  form2.submit();
  return false;
}

// ------------------------------------------
// Build system group mask
// ------------------------------------------
function createGroupMask(group)
{
  var mask = -1;

  // Anything to process
  if (group && group.length > 1)
  {
    // Reset mask
    mask = 0;
    // Loop on checkboxes
    for (i=0; i<group.length; i++)
    {
      // Checked
      if (group[i].checked)
      {
        // Add to mask
        mask += (1 << i);
      }
    }
  }

  // Return result
  return mask;
}

// ------------------------------------------
// Standard method to get an XML request object
// ------------------------------------------
function createXMLHttpRequest()
{
  try
  {
    return new ActiveXObject("Msxml2.XMLHTTP");
  }
  catch (e)
  {
  }

  try
  {
    return new ActiveXObject("Microsoft.XMLHTTP");
  }
  catch (e)
  {
  }
  try
  {
    return new XMLHttpRequest();
  }
  catch (e)
  {
  }

  return null;
 }

// ------------------------------------------
// Show a line route
// ------------------------------------------
function showRoute(uri, lang, city)
{
  var lnrtpic;

  // Get picture position
  var lnroute = document.getElementById('lnroute');
  if (!lnroute)
  {
      return;
  }

  // Get current data
  var sline = document.getElementById('first');
  var swday = document.getElementById('j');
  var shour = document.getElementById('h');

  // Process only for an actual line
  if (sline.selectedIndex < 0)
  {
      lnrtpic = '<img alt="" src="images/empty.gif" width="1" height="1"/>';
  }
  else
  {
    // Build picture url
    urlsrc = uri + '?a=t&g=' + lang + '&v=' + city + '&l=' + sline.options[sline.selectedIndex].value;
    if (swday && swday.options)
    {
      urlsrc += '&j=' + swday.options[swday.selectedIndex].value;
    }
    if (shour && swday.options)
    {
      urlsrc += '&h=' + shour.options[shour.selectedIndex].value;
    }

    // Make image tag
    urlpic   = urlsrc + '&o=g';
    lnrtpic  = '<img width="410" ';
    lnrtpic += 'alt="' + sline.options[sline.selectedIndex].text + '" ';
    lnrtpic += 'title="' + sline.options[sline.selectedIndex].text + '" ';
    lnrtpic += 'src="' +  urlpic + '" ';
    // lnrtpic += 'useMap="#corresp"';
    lnrtpic += '/>';

    /* // Prepare the image map
    urlmap   = urlsrc + '&o=m';
    lnrtpic += '\n<map name="corresp">\n';
    // Query the image map
    var xhReq = createXMLHttpRequest();
    if (xhReq != null)
    {
      xhReq.open("GET", urlmap, false);
      xhReq.send(null);
      lnrtpic += xhReq.responseText;
    }
    // close the image map
    lnrtpic += '\n</map>'; */
  }

  lnroute.innerHTML = lnrtpic;
}

// ------------------------------------------
// Show connections on a stop
// ------------------------------------------
function showConx(e, stop, conx)
{
  var ie = document.all
  var ns = document.getElementById && !document.all
  if (!ie && !ns)
  {
    return;
  }

  var curX = ((ns) ? e.pageX : event.clientX + ietruebody().scrollLeft) + 5;
  var curY = ((ns) ? e.pageY : event.clientY + ietruebody().scrollTop) + 5;

  var tooltip = document.getElementById('tooltip');

  tooltip.innerHTML = '<b>' + stop + '</b><br>' + conx;
  tooltip.style.left = "" + curX + "px";
  tooltip.style.top = "" + curY + "px";
  tooltip.style.visibility = "visible";
}


function ietruebody()
{
  return (document.compatMode && document.compatMode!="BackCompat") ? document.documentElement : document.body;
}

function hideConx()
{
  var tooltip = document.getElementById('tooltip');
  tooltip.style.visibility = "hidden";
  tooltip.style.left = "-1000px";
}

// ------------------------------------------
// Constructor for a stop in detailed route
// ------------------------------------------
function detStop(route, line, name, x, y, col)
{
  var rexp = new RegExp("´", "g");

  this.route = route;
  this.line  = line;
  // Restore standard quotes in name
  this.name  = name.replace(rexp, "\'");
  this.x     = x;
  this.y     = y;
  // Default color if absent
  if (col == null || col.length == 0)
  {
    col = '#000000';
  }
  this.col   = col;
}

// ------------------------------------------
// Google Maps: create map if not initialized
// ------------------------------------------
function createMap(city, point, zoom)
{
  // Compute map bounds for route
  for (i=0; i<crteList.length; i++)
  {
    if (crteList[i].y != 0 || crteList[i].x != 0)
    {
      if (crteList[i].x > maxe) maxe = crteList[i].x;
      if (crteList[i].x < maxw) maxw = crteList[i].x;
      if (crteList[i].y > maxn) maxn = crteList[i].y;
      if (crteList[i].y < maxs) maxs = crteList[i].y;
    }
  }
  for (i=0; i<srteList.length; i++)
  {
    if (srteList[i].x > maxe) maxe = srteList[i].x;
    if (srteList[i].x < maxw) maxw = srteList[i].x;
    if (srteList[i].y > maxn) maxn = srteList[i].y;
    if (srteList[i].y < maxs) maxs = srteList[i].y;
  }

  // Create map object
  googleMap = new GMap2(document.getElementById('map'));
  googleMap.enableScrollWheelZoom();

  // Select default map type
  var type = G_NORMAL_MAP;
  // if (city == "SANTIAGODECHILE")
  // {
  //   type = G_SATELLITE_MAP;
  // }

  // No point given: prepare for a route
  if (point == null)
  {
    // Get bounds
    var bound = new GLatLngBounds(new GLatLng(maxs, maxw),  new GLatLng(maxn, maxe));
    // Get center
    point = bound.getCenter();
    // Get optimal zoom level
    zoom = googleMap.getBoundsZoomLevel(bound);
  }

  // Set map position, zoom level & type
  googleMap.setCenter(point, zoom, type)

  // Add some controls
  googleMap.addControl(new GSmallMapControl());
  googleMap.addControl(new GMapTypeControl());
  /* googleMap.addControl(new GOverviewMapControl()); */

  // Create a custom icon
  iconMap = new GIcon();
  iconMap.image = "/simg/mapmarker.png";
  iconMap.shadow = "/simg/mapshadow.png";
  iconMap.iconSize = new GSize(20, 34);
  iconMap.shadowSize = new GSize(37, 34);
  iconMap.iconAnchor = new GPoint(9, 34);
  iconMap.infoWindowAnchor = new GPoint(9, 2);
  iconMap.infoShadowAnchor = new GPoint(18, 25);
}

// ------------------------------------------
// Google Maps: display map centered on 1 stop
// ------------------------------------------
function showMap(city, title, x, y)
{
  // Show map element(s)
  var hidmap = document.getElementById('hidmap');
  hidmap.style.visibility = "visible";
  hidmap.style.display = "inline";

  // Set map title (restoring quotes)
  var maptitle = document.getElementById('maptitle');
  var rexp = new RegExp("´", "g");
  maptitle.innerHTML = title.replace(rexp, "\'");

  // Create Version 2 map
  if (GBrowserIsCompatible())
  {
    // Get coordinates
    var point = new GLatLng(y, x);

    // Map not initialized yet
    if (googleMap == null)
    {
      createMap(city, point, 16);
    }
    else
    {
      // Remove existing overlays
      googleMap.clearOverlays();
      // Set optimal zoom level
      googleMap.setZoom(16);
      // Move map position
      googleMap.panTo(point);
    }

    // Add a marker on selected point
    var stop = new detStop("", "", title, x, y, '');
    var marker = createMarker(stop);
    googleMap.addOverlay(marker);
  }
}

// ------------------------------------------
// Google Maps: create a stop marker
// ------------------------------------------
function createMarker(stop)
{
  // Create a marker
  var marker = new GMarker(new GLatLng(stop.y, stop.x), iconMap);

  // Add listener
  GEvent.addListener(marker, "click", function() {
    marker.openInfoWindowHtml('<span class="txtbld">' + stop.name + '</span>');
  });

  return marker;
}

// ------------------------------------------
// Google Maps: display map with route
// ------------------------------------------
function showMapRoute(city, title, n)
{
  // Show map element(s)
  var hidmap = document.getElementById('hidmap');
  hidmap.style.visibility = "visible";
  hidmap.style.display = "inline";

  // Set map title
  var maptitle = document.getElementById('maptitle');
  maptitle.innerHTML = title;

  // Map not initialized yet
  if (googleMap == null)
  {
    // Create Version 2 map
    createMap(city, null, 0);
  }
  else
  {
    // Remove existing overlays
    googleMap.clearOverlays();
    // Get bounds
    var bound  = new GLatLngBounds(new GLatLng(maxs, maxw),  new GLatLng(maxn, maxe));
    // Get center
    var center = bound.getCenter();
    // Set optimal zoom level
    googleMap.setZoom(googleMap.getBoundsZoomLevel(bound));
    // Move map position
    googleMap.panTo(center);
  }

  // Build route overlay
  var leg    = 0;
  var index  = 0;
  var points = new Array();

  // Detailed route
  if (crteList.length > 0)
  {
    // Loop on stop list
    for (i=0; i<crteList.length && crteList[i].route<=n; i++)
    {
      // Requested route
      if (crteList[i].route == n)
      {
        // New leg
        if (crteList[i].line != leg)
        {
          // Draw leg if one is ready
          if (leg > 0)
          {
            // Display route on map
            googleMap.addOverlay(new GPolyline(points, crteList[i-1].col, 5, 0.8));
            // Empty leg points
            points.length = 0;
            index = 0;
          }

          // Not on first leg
          if (leg > 0 && crteList[i-1].name != crteList[i].name)
          {
            // Add marker to previous last stop
            googleMap.addOverlay(createMarker(crteList[i-1]));
          }

          // Add marker to first stop
          googleMap.addOverlay(createMarker(crteList[i]));

          // Next leg
          leg++;
        }

        // Add point
        if (crteList[i].y != 0 || crteList[i].x != 0)
        {
          points[index++] = new GLatLng(crteList[i].y, crteList[i].x);
        }
      }
    }
    // Complete leg
    if (leg != 0)
    {
      // Add marker to last stop
      googleMap.addOverlay(createMarker(crteList[i-1]));

      // Display last leg on map
      googleMap.addOverlay(new GPolyline(points, crteList[i-1].col, 5, 0.8));
    }
  }
  // Simplified route
  else
  {
    // Loop on stop list
    for (i=0; i<srteList.length-1 && srteList[i+1].route<=n; i++)
    {
      // Requested route
      if (srteList[i].route == n)
      {
        // Add marker to stop
        googleMap.addOverlay(createMarker(srteList[i]));
        // Create line between this stop and the next
        points[0] = new GLatLng(srteList[i].y, srteList[i].x);
        points[1] = new GLatLng(srteList[i+1].y, srteList[i+1].x);
        googleMap.addOverlay(new GPolyline(points, srteList[i].col, 5, 0.8));
      }
    }
    // Add arrival marker
    googleMap.addOverlay(createMarker(srteList[i]));
  }
}

// ------------------------------------------
// Google Maps: geocoding
// ------------------------------------------
function computeRoute(country, erradr)
{
  var chadr = null;
  var address1 = null;
  var address2 = null;
  var asearch1 = null;
  var asearch2 = null;
  var baddr1 = false;
  var baddr2 = false;
  var bfill1 = true;
  var bfill2 = true;

  // Get form
  var form   = document.getElementById('formcalc');
  var form2  = document.getElementById('formcalc2');

  // Prepare the special form
  var jsel = form.j;
  var hsel = form.h;
  form2.j.value = jsel.options[jsel.selectedIndex].value;
  form2.h.value = hsel.options[hsel.selectedIndex].value;
  form2.n.value = createGroupMask(form.m);
  // Walk preference
  form2.w.value = 1;
  if (form.w && form.w.checked)
  {
    form2.w.value = 2;
  }

  // Reset error fields
  clearErrAddress(true);
  clearErrAddress(false);

  // Check for address queries
  chadr = document.getElementById('adrdep');
  address1 = form.d.value;
  // Nothing entered
  if (address1.length == 0)
  {
    setFocus('first');
    return false;
  }
  // It is an address
  if (chadr && chadr.checked)
  {
    // Not yet geocoded
    if (address1.charAt(0) != '%')
    {
      // Geocoding required
      baddr1   = true;
      // Start not yet completed
      bfill1   = false;
      // Build search address
      asearch1 = address1 + ", " + country;
    }
    // Add prefix to address
    address1 = "@" + address1;
  }
  form2.d.value = address1;

  address2 = form.z.value;
  chadr = document.getElementById('adrarr');
  // Nothing entered
  if (address2.length == 0)
  {
    setFocus('ze');
    return false;
  }
  // It is an address
  if (chadr && chadr.checked)
  {
    // Not yet geocoded
    if (address1.charAt(0) != '%')
    {
      // Geocoding required
      baddr2   = true;
      // Start not yet completed
      bfill2   = false;
      // Build search address
      asearch2 = address2 + ", " + country;
    }
    // Add prefix to address
    address2 = "@" + address2;
  }
  form2.z.value = address2;

  // Create a Google geocoder
  if ((baddr1 || baddr2) && geocoder == null)
  {
    geocoder = new GClientGeocoder();
  }

  // Geocode the first address
  if (baddr1)
  {
    // Geocode
    geocoder.getLatLng(asearch1, function(point)
    // Result callback
    {
      // Not found
      if (!point)
      {
        errAddress(true, erradr);
        return false;
      }

      // Got an address
      bfill1 = true;
      // Record start stop
      form2.d.value = buildStop(address1, point);
      // If end stop is OK
      if (bfill2)
      {
        // Submit form
        form2.submit();
      }
    });
    /* Tests with a more complete option
    geocoder.getLocations(asearch1, function(response)
    // Result callback
    {
      // Error processing the query
      if (!response || response.Status.code != 200)
      {
        errAddress(true, erradr);
        return false;
      }

      // Got an address
      bfill1 = true;
      // Record start stop
      var place = response.Placemark[0];
      var point = new GLatLng(place.Point.coordinates[1], place.Point.coordinates[0]);
      errAddress(true, '' + place.address);
      form2.d.value = buildStop(address1, point);
      // If end stop is OK
      if (bfill2)
      {
        // Submit form
        form2.submit();
      }
    }); */
  }

  // Geocode the second address
  if (baddr2)
  {
    // Geocode
    geocoder.getLatLng(asearch2, function(point)
    // Result callback
    {
      // Not found
      if (!point)
      {
        errAddress(false, erradr);
        return false;
      }

      // Got an address
      bfill2 = true;
      // Record arrival stop
      form2.z.value = buildStop(address2, point);
      // If start stop is OK
      if (bfill1)
      {
        // Submit form
        form2.submit();
      }
    });
  }

  // Submit form (no geocoding required)
  if (!baddr1 && !baddr2)
  {
    form2.submit();
  }
  return false;
}

// ------------------------------------------
// Report an error on an address
// ------------------------------------------
function errAddress(depart, message)
{
  // Get the error message zone
  var error  = depart ? 'errdep' : 'errarr';
  var diverr = document.getElementById(error);

  // Present
  if (diverr)
  {
    // Set message text
    diverr.innerHTML = message;
    // Show message
    showLayer(error);
    // Set focus on field
    setFocus(depart ? 'first' : 'ze');
  }
}

// ------------------------------------------
// Clear error message
// ------------------------------------------
function clearErrAddress(depart)
{
  // Hide message
  hideLayer(depart ? 'errdep' : 'errarr');
}

// ------------------------------------------
// Build parameter for an address
// ------------------------------------------
function buildStop(address, point)
{
  var val;
  var stop = address;

  if (point)
  {
    stop  = address;
    val   = Math.round(point.x * 100000.0);
    stop += '%' + val;
    val   = Math.round(point.y * 100000.0);
    stop += '%' + val;
  }
  stop += "=";

  return stop;
}

// ------------------------------------------
// Clean-up address parameter for input field
// ------------------------------------------
function cleanStop(stop)
{
  var index, index2;
  var field = stop;

  // Check parameter
  if (field && field.length > 1)
  {
    // Find name delimiter
    index = field.indexOf('=');

    // Found one
    if (index >= 0)
    {
      // Get second part
      var subst = field.substring(index);
      // Check if it contains data
      if (subst.length > 1)
      {
        // Don't process further
        index = 0;
      }
    }

    // Address to parse
    if (index >= 0)
    {
      // Find coordinates delimiter
      index2 = field.indexOf('%');

      // Got one
      if (index2 >= 0)
      {
        // We will cut the string on this
        index = index2;
      }

      // Get address alone
      field = field.substring(0, index);
    }
  }

  return field;
}

// ------------------------------------------
// Set stop / address choice
// ------------------------------------------
function setAddress(bdep)
{
  var field = null;
  var ftext = null;
  var chadr = null;
  var chstp = null;

  // Process start stop
  if (bdep)
  {
    // field = document.getElementById('formcalc').d;
    field = document.getElementById('first');
    chstp = document.getElementById('stpdep');
    chadr = document.getElementById('adrdep');
  }
  // Process end stop
  else
  {
    // field = document.getElementById('formcalc').z;
    field = document.getElementById('ze');
    chstp = document.getElementById('stparr');
    chadr = document.getElementById('adrarr');
  }

  // Check if radio button are present  
  if (chstp && chadr && field)
  {
    ftext = field.value;

    // Check for address prefix
    if (ftext && ftext.length > 1 && ftext.charAt(0) == '@')
    {
      // Remove prefix
      field.value = ftext.substring(1);
      // Set checkbox
      chadr.checked = true;
    }
    /* else // Default behavior
    {
      // Set checkbox
      chstp.checked = true;
    } */
  }
}

// ------------------------------------------
// Hide maps
// ------------------------------------------
function hideMap()
{
  var hidmap = document.getElementById('hidmap');
  hidmap.style.visibility = "hidden";
  hidmap.style.display = "none";
}

// ------------------------------------------
// Request for "my location"
// ------------------------------------------
function checkLoc(depart)
{
  // Clear any error
  clearErrAddress(depart);

  // Departure selection
  if (depart)
  {
    chloc = document.getElementById('dl');
    field = document.getElementById('first');
  }
  // Arrival selection
  else
  {
    chloc = document.getElementById('el');
    field = document.getElementById('ze');
  }

  return true;
}

// ------------------------------------------
// Get user location with Loki API
// ------------------------------------------
function runLoki(field)
{
  // Initialize API object
  var loki = LokiAPI();

  // Set return code
  loki.onSuccess = function(location)
  {
    // alert(location.latitude + '-' + location.longitude);
    field.value = "%" + Math.round(location.longitude * 100000) + "%" + Math.round(location.latitude * 100000);
    // Unlock submit button
    document.getElementById('submit').disabled = false;
  }
  loki.onFailure = function(error)
  {
    alert("Location not found (" + error + ")");
  }

  // Set key
  loki.setKey('imetro.nanika.net');

  // Get user location
  loki.requestLocation(true, loki.NO_STREET_ADDRESS_LOOKUP);

  // Lock submit button
  document.getElementById('submit').disabled = true;
}

// ------------------------------------------
// Get MetroMobile RSS feed
// ------------------------------------------
var xhReq = null;
function loadFeed()
{
  /* xhReq = createXMLHttpRequest();
  if (xhReq != null)
  {
    xhReq.onreadystatechange = function() { processReqChange(); };
    xhReq.open("GET", "http://localhost/imetro/metromobile.xml", true);
    xhReq.send(null);
  } */
}

// ------------------------------------------
// Process XML request return
// ------------------------------------------
function processReqChange()
{
  if (xhReq.readyState == 4)
  {
    if (xhReq.status == 200)
    {
      if (xhReq.responseXML)
      {
        parseFeed(xhReq.responseXML);
      }
    }
  }
}

// ------------------------------------------
// Parse feed
// ------------------------------------------
function parseFeed(dom)
{
  var sdate = '';
  var cont  = '';
  var elfl  = document.getElementById('feed');
  elfl.innerHTML = '';

  var nl = xhReq.responseXML.getElementsByTagName('item');
  for (var i=0; i<nl.length && i<3; i++)
  {
    var nli   = nl.item(i);
    var title = getNodeValue(nli, 'title');
    var link  = getNodeValue(nli, 'link');
    var desc  = getNodeValue(nli, 'description');
    var date  = getNodeValue(nli, 'pubDate');
    var ndate = monthtext[date.getMonth()] + ' ' + date.getDay() + ', ' + date.getFullYear();
    cont += '<h2>' + ndate + '</h2>';
    cont += '<h2><a href="' + link + '">' + title + '</a></h2>';
    // cont += '<p class="txtsml">' + desc + '</p>';
  }
  elfl.innerHTML = cont;
}

// ------------------------------------------
// Get an item from an XML element
// ------------------------------------------
function getNodeValue(elt, tag)
{
  // Get element
  var elem = elt.getElementsByTagName(tag);

  // Check validity
  if (elem && elem.length > 0)
  {
    return elem.item(0).firstChild.nodeValue;
  }

  return null;
}
