//NOTE includers of this file must also include cookie.js


// ---------------- map global variables --------------------------

var MAP_WIDTH = 575;
var MAP_HEIGHT = 575;
var DEFAULT_MAP_LAYERS = "%2BARTERIALCONSTRUCTION%2BEXPRESSWAYCONSTRUCTION%2BANNOTATIONS%2BEXPRESSWAYCONGESTION%2BARTERIALCONGESTION%2BINCIDENTS%2BTRAVELTIMES%2BAIRPORTS%2BMESSAGES%2BCAMERAS%2BSPECIALEVENTS%2BHIGHLIGHTS";
// reload the page every RELOAD_INTERVAL if https (for ComCenter)
var RELOAD_INTERVAL = 1000 * 60;
// should match levels in config/gui/map/ZoomManager.xml (reread in map.jsp just in case)
var ZOOM_LEVELS = [0.001,0.002,0.004,0.008,0.016,0.032,0.064,0.128,0.256,0.512,1.024];
// home map and layer cookies expiration date: 90 days from today
var MAP_COOKIE_EXPIRES = new Date();
MAP_COOKIE_EXPIRES.setTime(MAP_COOKIE_EXPIRES.getTime() + (60 * 60 * 24 * 90 * 1000));

//--- global variables to maintain drag state during pan ---
var panning = false;
// the starting point relative to the document (for moving the map image)
var docStartX;
var docStartY;
// the original starting point of drag, relative to document, not reset after redraws
var originX;
var originY;
// the starting point relative to page on an iOS (iPhone, iPad, iPod) device when user initiates a 3-finger gesture (third finger detected)
var docStartiPhonePageX;
var docStartiPhonePageY;
// the end point relative to page on an iOS device when user when user lifts any finger
var docEndiPhonePageX;
var docEndiPhonePageY;



// the current map state, set in setMainMap, global for event handlers; fields are the current zoomLevel, x, and y parameters
// for map?type=image and a map object for name, constants and defaults
var currentMap = new Object();

// map objects: properties are map/cookie name and default map parameters (used by initializeMapFromName from map.jsp)
var home = {
  name: "Home",
  zoomLevel: 3,
  x: 335800,
  y: 576841
};
//NOTE these are reset by initializeMapFromId so that pan("reset") does the right thing
//IESUXTAG call this one eventMap so it doesn't clash with the global
var eventMap = {
  name: "Event",
  zoomLevel: 3,
  x: 335800,
  y: 576841
};
var chicagoArea = {
  name: "Chicago Area",
  zoomLevel: 3,
  x: 335800,
  y: 577841
};
var chicagoCity = {
  name: "City of Chicago",
  zoomLevel: 4,
  x: 349361,
  y: 572965
};
var lakeCounty = {
  name: "Lake County IL",
  zoomLevel: 4,
  x: 330109,
  y: 628027
};
var gary = {
  name: "Gary",
  zoomLevel: 3,
  x: 393849,
  y: 531176
};
var milwaukee = {
  name: "Milwaukee",
  zoomLevel: 4,
  x: 326575,
  y: 701695
};
var rockford = {
  name: "Rockford",
  zoomLevel: 4,
  x: 241463,
  y: 626255
};
var madison = {
  name: "Madison",
  zoomLevel: 4,
  x: 214509,
  y: 716101
};
var quadcities = {
  name: "Quad Cities",
  zoomLevel: 4,
  x: 114665,
  y: 537250
};
var michigan = {
  name: "SW Michigan",
  zoomLevel: 2,
  x: 525049,
  y: 619966
};
MAPS = [home, eventMap, chicagoArea, chicagoCity, lakeCounty, gary, milwaukee, rockford, madison, quadcities, michigan];

// layer objects, visible is the current state of each (Roads, Lakes, and Counties are always on)
//  names must match layer names in config/gui/map/GLayerLoader.xml
var arterial_construction = {
  visible: true,
  name: "ArterialConstruction",
  minimumZoom: 0
};
var expressway_construction = {
  visible: true,
  name: "ExpresswayConstruction",
  minimumZoom: 0
};
var annotations = {
  visible: true,
  name: "Annotations",
  minimumZoom: 0
};
var arterial_congestion = {
  visible: true,
  name: "ArterialCongestion",
  minimumZoom: 0
};
var expressway_congestion = {
  visible: true,
  name: "ExpresswayCongestion",
  minimumZoom: 0
};
var incidents = {
  visible: true,
  name: "Incidents",
  minimumZoom: 0
};
var travelTimes = {
  visible: true,
  name: "TravelTimes",
  minimumZoom: 0
};
var airports = {
  visible: true,
  name: "Airports",
  minimumZoom: 0
};
var messageSigns = {
  visible: true,
  name: "Messages",
  minimumZoom: 0
};
var cameras = {
  visible: true,
  name: "Cameras",
  minimumZoom: 0
};
var specialEvents = {
  visible: true,
  name: "SpecialEvents",
  minimumZoom: 0
};
LAYERS = [expressway_construction, arterial_construction, annotations, expressway_congestion, arterial_congestion,
  incidents, travelTimes, airports, messageSigns, cameras, specialEvents];

// true indicates a colorblind user for whom uncongested is painted blue
colorblind = false;

// ---------------------- main map functions ------------------------

// writes the MyMap message if there is no "Home" cookie
function writeMyMapMessage(map) {
  if (map.name == "Home" && getCookieValue("Home") == null) {
    document.writeln("<div style='background-color: rgb(242, 242, 242); border: 1px solid rgb(189, 190, 195); "
      + "padding: 5px; width: 280px; font-size: 11px;'>Welcome to the Travel Midwest web site.  The home page "
      + "displays a map that you can scroll or zoom to your area of interest.  When you return to the site or "
      + "select \"My Map\" from the \"Maps\" menu, the map will display that area.  (This information is saved "
      + "in a cookie on your browser.)  Whether each map layer is visible is also saved.</div>");
  }  
}

// set MAP_WIDTH and MAP_HEIGHT based on the window size (called at top of map.jsp and by resizeMap)
function setMapDimensions() {
	if (typeof(window.innerWidth) == 'number') {
		// standards (w3c) complaint browser
		MAP_WIDTH = window.innerWidth - 35;
		MAP_HEIGHT = window.innerHeight - 125 - 10;
	}
	else if (document.documentElement
	    && (document.documentElement.clientWidth || document.documentElement.clientHeight)) {
		// internet explorer (something is different about the margins on IE ...)
		MAP_WIDTH = document.documentElement.clientWidth - 20;
		MAP_HEIGHT = document.documentElement.clientHeight - 125 - 10;
	}
	else {
		MAP_WIDTH = 575;
		MAP_HEIGHT = 575;
	}
	if (MAP_WIDTH < 600) {
	  MAP_WIDTH = 600;
	}
	if (MAP_HEIGHT < 545) {
	  MAP_HEIGHT = 545;
	}
  // mapViewport isn't there on the first call during page layout
  if (document.getElementById("mapViewport")) {
    document.getElementById("mapViewport").width = MAP_WIDTH;
    document.getElementById("mapViewport").height = MAP_HEIGHT;
  }
  window.onresize = resizeMap;
}

// handler for window resize event
function resizeMap(e) {
	setMapDimensions();
  loadMap();
}

// sets currentMap fields from the argument map object, possibly overridden from the map cookie, then calls initializeMap()
function initializeMapFromName(map) {
  currentMap.name = map.name;
  // set currentMap.zoomLevel, x, and y from the cookie if any;
  // otherwise set them from the argument map
  var params =  getCookieValue(currentMap.name);
  if (params != null) {
    var start = params.indexOf("zoomLevel:") + 10;
    var end = params.indexOf("|", start);
    currentMap.zoomLevel = params.substring(start, end);
    start = params.indexOf("x:") + 2;
    end = params.indexOf("|",start);
    currentMap.x = params.substring(start,end);
    start = params.indexOf("y:") + 2;
    end = params.indexOf("|",start);
    currentMap.y = params.substring(start,end);
  }
  else {
    currentMap.zoomLevel = map.zoomLevel;
    currentMap.x = map.x;
    currentMap.y = map.y;
  }
  currentMap.id = null;
  initializeMap();
}

// sets currentMap.x, y, and zoomlevel to the arguments, and currentMap.name to "event" (for MapPageController)
// then calls initializeMap() (used for milepost maps)
function initializeMapFromLocation(x, y, zoomLevel) {
  currentMap.x = x;
  currentMap.y = y;
  currentMap.zoomLevel = zoomLevel;
  currentMap.name = "Event";
  currentMap.id = null;
  initializeMap();
}

// sets currentMap.id to the argument, currentMap.zoomLevel to 9, and currentMap.name to "Event" (for MapPageController)
// then calls initializeMap()
function initializeMapFromId(id) {
  currentMap.id = id;
  currentMap.name = "Event";
  // get the initial zoom level for a map that encloses the event or camera
  var request = new XMLHttpRequest;
  var url = "map?type=location&width=" + MAP_WIDTH + "&height=" + MAP_HEIGHT + "&id=" + id;
  request.open("GET", url, true);
  request.onreadystatechange = function() {
    if (request.readyState == 4) {
      if (request.status == 200) {
        if (request.responseText.length == 0) {
          alert("Invalid event ID");
        }
        else {
          var params = request.responseText.split(",");
          currentMap.x = params[0];
          currentMap.y = params[1];
          currentMap.zoomLevel = params[2];
          // set eventMap object values for pan("reset")
          eventMap.x = params[0];
          eventMap.y = params[1];
          eventMap.zoomLevel = params[2];
          initializeMap();
        }
      }
      else {
        alert("Error getting event location");
      }
    }

  };
  request.send(null);
}

// sets LAYERS[].visible from the "mapLayers" cookie, sets colorblind from the "colorblind" cookie, sets the "mainmap"
// src URL (loadMap), sets whether the map panels are visible, sets the contents of the infoViewer from the
// "infoViewer" cookie, and register event handlers
function initializeMap() {
  // set map layers from the mapLayers cookie if any, otherwise from DEFAULT_MAP_LAYERS, and reset images
  var mapLayers =  getCookieValue("mapLayers");
  if (mapLayers == null) {
    mapLayers = DEFAULT_MAP_LAYERS;
  }
  for (var ix = 0; ix < LAYERS.length; ix++) {
    LAYERS[ix].visible = (mapLayers.indexOf("%2B" + LAYERS[ix].name.toUpperCase()) != -1);
    setLayerImage(LAYERS[ix]);
  }
  //REDTAG someone might still have an old single congestion layer cookie
  if (mapLayers.indexOf("%2BCONGESTION") != -1) {
    arterial_congestion.visible = true;
    expressway_congestion.visible = true;
    setLayerImage(arterial_congestion);
    setLayerImage(expressway_congestion);
    setCookie("mapLayers", getMapLayersValue(), MAP_COOKIE_EXPIRES);
  }
  colorblind = (getCookieValue("colorblind") == "true");
  loadMap();
  // set a timer to reload the page every RELOAD_INTERVAL if https (for ComCenter)
  if (document.location.protocol.indexOf("https") != -1) {
    //REDTAG put the time so that it appears to be a different URL, otherwise the image won't reload
    setInterval("document.images['mainmap'].src = getMainMapUrl('image') + '&time=' + new Date().getTime()", RELOAD_INTERVAL);
  }
  // set map panels visibility from the mapPanels cookie if any
  var mapPanels =  getCookieValue("mapPanels");
  // null cookie means nothing was ever set, empty string means the user closed everything
  if (mapPanels != null) {
    setPanelVisible("layers", mapPanels.indexOf("layers|") != -1);
    setPanelVisible("legend", mapPanels.indexOf("legend|") != -1);
    setPanelVisible("viewer", mapPanels.indexOf("viewer|") != -1);
    setInfoViewerHeight();
  }

  // sets the contents of the infoViewer from the "infoViewer" cookie
  var infoViewerValue = getCookieValue("infoViewer");
  if (infoViewerValue != null && infoViewerValue != "") {
    var itemParamsList = infoViewerValue.split("|");
    addInfoViewerItemFromParams(itemParamsList, 0);
  }
  
  //NOTE onclick done by mouseReleased when x/ydown = x/yup
  document.getElementById("mapViewport").onmousedown = mousePressed;
  document.onmousemove = mouseMoved;
  document.onmouseup = mouseReleased;
   
  // iPhone touch gesture event handling
  if ((navigator.userAgent.match(/iPhone/i)) || (navigator.userAgent.match(/iPod/i)) || (navigator.userAgent.match(/iPad/i))) {
    document.getElementById("mapViewport").ontouchstart = iPhoneTouchStart;
    document.ontouchmove = iPhoneTouchMove;
    document.ontouchend = iPhoneTouchEnd;
    document.ontouchcancel = iPhoneTouchCancel;
  }
  
  document.images['mainmap'].onload = mapLoaded;
}

// refreshes the map image from getMainMapUrl("image")
function loadMap() {
  document.images['mainmap'].src = getMainMapUrl("image");
}

// returns the URL for the map image or the MapInformation window
function getMainMapUrl(type) {
  return "map?type=" + type + "&zoomLevel=" + currentMap.zoomLevel
    + "&width=" + MAP_WIDTH + "&height=" + MAP_HEIGHT + "&layers=" + getMapLayersValue()
    + "&x=" + currentMap.x + "&y=" + currentMap.y + (colorblind ? "&ada=true" : "")
    + (currentMap.id != null ? ("&id=" + currentMap.id) : "");
}

// returns the value of the layers parameter for map from mapLayersXXX globals,
// also used as the value of the "mapLayers" cookie
function getMapLayersValue() {
  // background, parks, road, lake and county layers always on
  var layers = "%2BBACKGROUND%2BPARKS%2BROADS%2BLAKES%2BCOUNTIES%2BSTATES%2BHIGHLIGHTS";
  for (var ix = 0; ix < LAYERS.length; ix++) {
    if (LAYERS[ix].visible) layers += "%2B" + LAYERS[ix].name.toUpperCase();
  }
  return layers;
}

// ---------------------- main map interaction functions ------------------------

// sets up for pan (or click)
function mousePressed(event) {
  // W3C passes the event to the handler, IE uses the global window.event
  event = ((event == null) ? window.event : event);
  if (isLeftButton(event.button)) {
    docStartX = event.clientX + document.body.scrollLeft;
    docStartY = event.clientY + document.body.scrollTop;
    originX = docStartX;
    originY = docStartY;
    panning = true;
    preventDefault(event);
  }
}

// moves the image with the mouse drag
function mouseMoved(event) {
  // W3C passes the event to the handler, IE uses the global window.event
  event = ((event == null) ? window.event : event);
  if (isLeftButton(event.button) && panning) {
    var docNextX =  event.clientX + document.body.scrollLeft;
    var docNextY =  event.clientY + document.body.scrollTop;
    var mapDiv = document.getElementById("mapDiv");
    mapDiv.style.left = (docNextX - docStartX) + "px";
    mapDiv.style.top = (docNextY - docStartY) + "px";
    preventDefault(event);
  }
}

// done panning, reloads map
function mouseReleased(event) {
  // W3C passes the event to the handler, IE uses the global window.event
  event = ((event == null) ? window.event : event);
  if (isLeftButton(event.button) && panning) {
    var docEndX =  event.clientX + document.body.scrollLeft;
    var docEndY =  event.clientY + document.body.scrollTop;
    if (originX == docEndX && originY == docEndY) {
      mouseClicked(event);
    }
    else if (docEndX != docStartX && docEndY != docStartY) {
      // MapServlet requires ints
      var xOffset = Math.round((docEndX - docStartX) / ZOOM_LEVELS[currentMap.zoomLevel]);
      var yOffset = Math.round((docStartY - docEndY) / ZOOM_LEVELS[currentMap.zoomLevel]);
      currentMap.x = currentMap.x - xOffset;
      currentMap.x = Math.max(currentMap.x, mapInformation.minX);
      currentMap.x = Math.min(currentMap.x, mapInformation.maxX);
      currentMap.y = currentMap.y - yOffset;
      currentMap.y = Math.max(currentMap.y, mapInformation.minY);
      currentMap.y = Math.min(currentMap.y , mapInformation.maxY);
      setMapCookie();
      loadMap();  
    }
    preventDefault(event);
    panning = false;
  }
}

// sets up map for dragging if a 3-finger gesture
function iPhoneTouchStart(event) {
  if (event.targetTouches.length == 3) {
    docStartiPhonePageX = event.targetTouches[0].pageX;
    docStartiPhonePageY = event.targetTouches[0].pageY;
    panning = true;
  }
}

// moves the image with 3-finger drag
function iPhoneTouchMove(event) {
  if (panning) {
    docEndiPhonePageX = event.targetTouches[0].pageX;
    docEndiPhonePageY = event.targetTouches[0].pageY;
    var mapDiv = document.getElementById("mapDiv");
    mapDiv.style.left = (docEndiPhonePageX - docStartiPhonePageX) + "px";
    mapDiv.style.top = (docEndiPhonePageY - docStartiPhonePageY) + "px";
  }
}

// any finger lifted ends the panning mode
function iPhoneTouchEnd(event) {
  if (panning && event.targetTouches.length < 3) {
    var xOffset = Math.round((docEndiPhonePageX - docStartiPhonePageX) / ZOOM_LEVELS[currentMap.zoomLevel]);
    var yOffset = Math.round((docStartiPhonePageY - docEndiPhonePageY) / ZOOM_LEVELS[currentMap.zoomLevel]);
    currentMap.x = ((currentMap.x > xOffset) ? currentMap.x - xOffset : 0);
    currentMap.y = ((currentMap.y > yOffset) ? currentMap.y - yOffset : 0);
    setMapCookie();
    loadMap();
    window.location.reload();
    panning = false;
  }
}

function iPhoneTouchCancel(event) {
  if (panning) {
    panning = false;
  }
}

//W3C uses preventDefault(), IE uses the returnValue field
function preventDefault(event) {
  if (event.preventDefault) {
    event.preventDefault();
  }
  else {
    event.returnValue = false;
  }
}

// puts the mapDiv back where it was before the drag (after the new image is loaded)
// onload handler for the mainmap image
function mapLoaded() {
  document.getElementById("mapDiv").style.left = "0px";
  document.getElementById("mapDiv").style.top = "0px";
  document.getElementById("mapDiv").width = MAP_WIDTH;
  document.getElementById("mapDiv").height = MAP_HEIGHT;
  document.images['mainmap'].width = MAP_WIDTH;
  document.images['mainmap'].height = MAP_HEIGHT;
}

// click on main map: handle the data returned from <host>/lmiga/map?type=data...  
// If the data is a "no information" message, place it in an alert.  If it begins with "window.open",
// it is JavaScript to be executed.  Otherwise, it is a table to be placed in the Info Viewer on the map page.
// (It is not actually an event handler anymore: triggered by mouseReleased when mouse start and end are equal.)
function mouseClicked(event) {
  // W3C passes the event to the handler, IE uses the global window.event
  event = ((event == null) ? window.event : event);
  // layerX for Firefox, offsetX for IE
  clickX = (event.layerX === undefined ? event.offsetX : event.layerX);
  clickY = (event.layerY === undefined ? event.offsetY : event.layerY); 
  var request = new XMLHttpRequest;
  var itemUrl = getMainMapUrl("data") + "&clickx=" + clickX + "&clicky=" + clickY;
  request.open("GET", itemUrl, true);
  request.onreadystatechange = function () {
    if (request.readyState == 4) {
      if (request.status == 200) {
        var info = request.responseText;
        if (info.indexOf("window.open") == 0) {
          eval(info);
        }
        else if (info.indexOf("<table class='viewer'>") == 0) {
          addInfoViewerItem(info, itemUrl);
        }
        else {
          alert(info);
        }
      }
      else {
        alert("There was a problem loading the map information");
      }
    }
  };
  request.send(null);
  return false;
}

function isLeftButton(button) {
  // why can't we all get along???
  return navigator.userAgent.indexOf("MSIE") == -1 ? (button == 0) : (button == 1);
}


//---------------------- map panel functions ------------------------

// hides or shows the given info panel, toggles the corresponding img, sets the "mapPanels"
// cookie, and sets the Info Viewer height
function togglePanel(panel) {
  var img = document.getElementById(panel + "Img");
  setPanelVisible(panel, img.src.indexOf("images/collapsed.gif") != -1);
  setMapPanelsCookie();
  setInfoViewerHeight();
}

// sets the "mapPanels" cookie depending on which map panels are visible
function setMapPanelsCookie() {
  var cookieValue = "";
  if (document.getElementById("layers").style.display == "") {
    cookieValue += "layers|";
  }
  if (document.getElementById("legend").style.display == "") {
    cookieValue += "legend|";
  }
  if (document.getElementById("viewer").style.display == "") {
    cookieValue += "viewer|";
  }
  setCookie("mapPanels", cookieValue);  
}

//hides or shows the given info panel
function setPanelVisible(panel, visible) {
  var img = document.getElementById(panel + "Img");
  document.getElementById(panel).style.display = (visible ? "" : "none");
  img.src = (visible ? "images/expanded.gif" : "images/collapsed.gif");
  var panelDesc = panel;
  if (panelDesc == "layers") {
    panelDesc = "layer and zoom controls";
  }
  else if (panelDesc == "viewer") {
    panelDesc = "info viewer";
  }
  img.alt = (visible ? "hide " : "show ") + panelDesc;
  img.title = (visible ? "click to hide" : "click to show");
  // the link next to the image
  if (navigator.userAgent == null || navigator.userAgent.indexOf("MSIE") == -1) {
    //IESUXTAG expression gives "Object doesn't support this property or method" with IE
    img.parentNode.childNodes.item(3).title = (visible ? "click to hide" : "click to show");
  }
}

// toggles the given layer (onclick on map.jsp buttons)
function toggleLayer(layer) {
  layer.visible = !layer.visible;
  // set the "mapLayers" cookie from LAYERS globals
  setCookie("mapLayers", getMapLayersValue(), MAP_COOKIE_EXPIRES);
  loadMap();
  setLayerImage(layer);
}

// sets the layer button image in map.jsp based on layer.visible
function setLayerImage(layer) {
  // got rid of toggling the airport layer, but it might still be in someone's cookie
  if (layer.name != "Airports") {
    filenameSuffix = ZOOM_LEVELS[currentMap.zoomLevel] < layer.minimumZoom ? "Disabled.gif" : ".gif";
    status = ZOOM_LEVELS[currentMap.zoomLevel] < layer.minimumZoom ? " and disabled" : "";
    var img = document.images[layer.name];
    if (layer.visible) {
      img.src = "images/" + layer.name + "On" + filenameSuffix;
      img.alt = "turn " + layer.name + " on";
      img.title = layer.name + " layer is on" + status;
    }
    else {
      img.src = "images/" + layer.name + "Off" + filenameSuffix;
      img.alt = "turn " + layer.name + " off";
      img.title = layer.name + " layer is off" + status;
    }
  }
}

//zooms the map in (onclick on the zoom in map tool in map.jsp)
function zoomIn() {
  if (currentMap.zoomLevel < ZOOM_LEVELS.length - 1) {
    ++currentMap.zoomLevel;
  }
  setMapCookie();
  loadMap();
  for (var i = 0; i < LAYERS.length; i++) {
    setLayerImage(LAYERS[i]);
  }
}

// zooms the map out (onclick on the zoom out map tool in map.jsp)
function zoomOut() {
  if (currentMap.zoomLevel > 0) {
    --currentMap.zoomLevel;
  }
  setMapCookie();
  loadMap();
  for (var i = 0; i < LAYERS.length; i++) {
    setLayerImage(LAYERS[i]);
  }
}

// pans the map 1/3 of its width or height in the given direction, or for "reset"
// resets the map to the original zoom and location
function pan(direction) {
  var xOffset = Math.round((MAP_WIDTH / 3) / ZOOM_LEVELS[currentMap.zoomLevel]);
  var yOffset = Math.round((MAP_HEIGHT / 3) / ZOOM_LEVELS[currentMap.zoomLevel]);
  switch (direction) {
  case "left":
    currentMap.x = Math.max(currentMap.x - xOffset, mapInformation.minX);
    break;
  case "right":
    //HACKTAG every so often, JavaScript thinks currentMap.x is a String
    currentMap.x = Math.min(new Number(currentMap.x) + xOffset, mapInformation.maxX);
    break;
  case "down":
    currentMap.y = Math.max(currentMap.y - yOffset, mapInformation.minY);
    break;
  case "up":
    //HACKTAG every so often, JavaScript thinks currentMap.y is a String
    currentMap.y = Math.min(new Number(currentMap.y) + yOffset, mapInformation.maxY);
    break;
  case "reset":
    for (var ix = 0; ix < MAPS.length; ix++) {
      if (currentMap.name == MAPS[ix].name) {
        currentMap.x = MAPS[ix].x;
        currentMap.y = MAPS[ix].y;
        currentMap.zoomLevel = MAPS[ix].zoomLevel;
        break;
      }
    }
    break;
  }
  setMapCookie();
  loadMap();
}

// sets the cookie from currentMap.zoomLevel, x, and y, with MAP_COOKIE_EXPIRES for the Home map
function setMapCookie() {
  setCookie(currentMap.name,
    "zoomLevel:" + currentMap.zoomLevel + "|x:" + currentMap.x + "|y:" + currentMap.y + "|",
    currentMap.name == "Home"? MAP_COOKIE_EXPIRES : null);
}


//---------------------- Info Viewer functions ------------------------

// with all layers on, the layers parameter value is 186 characters.  Use abbreviations in the cookies.
var LETTER_FOR_LAYER = {
  AIRPORTS: "a",
  ANNOTATIONS: "n",
  BACKGROUND: "b",
  CAMERAS: "c",
  ARTERIALCONGESTION: "k",
  EXPRESSWAYCONGESTION: "g",
  ARTERIALCONSTRUCTION: "e",
  EXPRESSWAYCONSTRUCTION: "o",
  COUNTIES: "u",
  HIGHLIGHTS: "h",
  INCIDENTS: "i",
  LAKES: "l",
  MESSAGES: "m",
  PARKS: "p",
  ROADS: "r",
  SPECIALEVENTS: "s",
  STATES: "x",
  TRAVELTIMES: "t"
};
var LAYER_FOR_LETTER = {
  a: "%2BAIRPORTS",
  n: "%2BANNOTATIONS",
  b: "%2BBACKGROUND",
  c: "%2BCAMERAS",
  k: "%2BARTERIALCONGESTION",
  g: "%2BEXPRESSWAYCONGESTION",
  e: "%2BARTERIALCONSTRUCTION",
  o: "%2BEXPRESSWAYCONSTRUCTION",
  u: "%2BCOUNTIES",
  h: "%2BHIGHLIGHTS",
  i: "%2BINCIDENTS",
  l: "%2BLAKES",
  m: "%2BMESSAGES",
  p: "%2BPARKS",
  r: "%2BROADS",
  s: "%2BSPECIALEVENTS",
  x: "%2BSTATES",
  t: "%2BTRAVELTIMES"
};

// makes a request for the ix'th itemParams and include a call to
// addInfoViewerItemFromParams(itemParamsList, ix + 1) in the onreadystatechange handler so the next
// item is added after this one;  after all items are added, opens the Info Viewer and sets its height
function addInfoViewerItemFromParams(itemParamsList, ix) {
  if (ix < itemParamsList.length) {
    var request = new XMLHttpRequest;
    request.open("GET", getInfoViewerItemUrl(itemParamsList[ix]), true);
    request.onreadystatechange = function() {
      if (request.readyState == 4) {
        if (request.status == 200) {
          var info = request.responseText;
          document.getElementById("viewerDiv").innerHTML = document.getElementById("viewerDiv").innerHTML + info;
          addInfoViewerItemFromParams(itemParamsList, ix + 1);
        }
        else {
          alert("There was a problem loading the Info Viewer");
        }
      }

    };
    request.send(null);
  }
  else {
    addCloseAll();
    setInfoViewerHeight();
  }
}

// returns the URL for "itemParams" from the "infoViewer" cookie to use to get the HTML for the item
// "zoomlevel,width,height,layerAbbreviation,x,y,clickx,clicky" or "width,height,layerAbbreviation,id,clickx,clicky"
function getInfoViewerItemUrl(itemParams) {
  //"zoomlevel,width,height,layerAbbreviation,x,y,clickx,clicky" as written by getInfoViewerItemParams
  var params = itemParams.split(",");
  var layersAbbrev = (params.length == 8 ? params[3] : params[2]);
  var layers = "";
  for (var ix = 0; ix < layersAbbrev.length; ix++) {
    layers += LAYER_FOR_LETTER[layersAbbrev.charAt(ix)];
  }
  var url;
  if (params.length == 8) {
    url = "map?type=data&zoomLevel=" + params[0] + "&width=" + params[1] + "&height=" + params[2] + "&layers=" + layers
      + "&x=" + params[4] + "&y=" + params[5] + "&clickx=" + params[6] + "&clicky=" + params[7];
  }
  else {
    // id URL
    url = "map?type=data&width=" + params[0] + "&height=" + params[1] + "&layers=" + layers
      + "&id=" + params[3] + "&clickx=" + params[4] + "&clicky=" + params[5];
  }
  return url;
}

// adds the table "info" to the Info Viewer on the map page and adds the abbreviation for "itemUrl"
// to the "infoViewer" cookie
function addInfoViewerItem(info, itemUrl) {
  var viewerDiv = document.getElementById("viewerDiv");
  // remove the "Close All" button table, and add it again after adding the item
  if (viewerDiv.childNodes.length != 0) {
    viewerDiv.removeChild(viewerDiv.firstChild);
  }
  viewerDiv.innerHTML = info + viewerDiv.innerHTML;
  addCloseAll();
  setPanelVisible("viewer", true);
  setMapPanelsCookie();
  setInfoViewerHeight();
  var itemParams = getInfoViewerItemParams(itemUrl);
  var infoViewerValue = getCookieValue("infoViewer");
  if (infoViewerValue == null || infoViewerValue == "") {
    infoViewerValue = itemParams;
  }
  else {
    infoViewerValue = itemParams + "|" + infoViewerValue;
  }
  setCookie("infoViewer", infoViewerValue);
}

// returns the abbreviation for "itemUrl" to use in the "infoViewer" cookie (map?type=data URLs are 281 chars)
// "zoomlevel,width,height,layerAbbreviation,x,y,clickx,clicky" or "width,height,layerAbbreviation,id,clickx,clicky"
function getInfoViewerItemParams(itemUrl) {
  // URL as written by getMainMapUrl("data") + "&clickx=" + clickX + "&clicky=" + clickY in mouseClicked
  var regex = /map\?type=data&zoomLevel=(.*)&width=(.*)&height=(.*)&layers=(.*)&x=(.*)&y=(.*)&clickx=(.*)&clicky=(.*)/;
  var idRegex = /map\?type=data&width=(.*)&height=(.*)&layers=(.*)&id=(.*)&clickx=(.*)&clicky=(.*)/;
  var value = itemUrl.replace(regex, "$1,$2,$3,[$4],$5,$6,$7,$8");
  if (value == itemUrl) {
    // didn't match, must be an id URL
    value = itemUrl.replace(idRegex, "$1,$2,[$3],$4,$5,$6");
  }
  var layers = value.substring(value.indexOf("[") + 1, value.indexOf("]")).split("%2B");
  var layersAbbrev = "";
  // the first element from the split is an empty string
  for (var ix = 1; ix < layers.length; ix++) {
    layersAbbrev += LETTER_FOR_LAYER[layers[ix]];
  }
  return value.substring(0, value.indexOf("[")) + layersAbbrev + value.substring(value.indexOf("]") + 1);
}

// sets the Info Viewer height to the height of the  minus the heights of the other panels
function setInfoViewerHeight() {
  var viewerDiv = document.getElementById("viewerDiv");
  // the total of the heights of the rows in the viewer table besides the info viewer
  var otherHeight = 0;
  var rows = document.getElementById("viewerTable").rows;
  for (var row = 0; row < rows.length; row++) {
    if (rows[row].id != "viewer") {
      otherHeight += rows[row].offsetHeight;
    }
  }
  var height = MAP_HEIGHT - otherHeight;
  if (height < 0 || viewerDiv.childNodes.length == 0) {
    height = 0;
  }
  //HMMM takes time for offsetHeight to propagate after adding table?
  //height = Math.min(height, viewer.offsetHeight);
  viewerDiv.style.height = height + "px";
  //IESUXTAG stoopid IE puts the scrollbar next to the margin not in it so it expands the div
  viewerDiv.style.width = "290px";
}

// removes the InfoViewer table containing this button and removes the corresponding URL from
// the "infoViewer" cookie
function removeInfoViewerItem(button) {
  var table = button.parentNode.parentNode.parentNode.parentNode;
  // the index of this item, to remove it from the "infoViewer" cookie value
  var ix = 0;
  var sibling = table.previousSibling;
  while (sibling != null) {
    ix++;
    sibling = sibling.previousSibling;
  }
  if (getCookieValue("infoViewer") != null) {
    var infoViewerUrls = getCookieValue("infoViewer").split("|");
    if (infoViewerUrls.length == 1) {
      setCookie("infoViewer", "");
    }
    else {
      // the table's index index in the cookie is 1 less because the "Close All"" table is not in the cookie
      infoViewerUrls.splice(ix - 1, 1);
      var infoViewerValue = "";
      for (ix = 0; ix < infoViewerUrls.length; ix++) {
        infoViewerValue += (infoViewerUrls[ix] + "|");
      }
      setCookie("infoViewer", infoViewerValue.substring(0, infoViewerValue.length - 1));
    }
  }
  var viewerDiv = document.getElementById("viewerDiv");
  viewerDiv.removeChild(table);
  // the "Close All" table is all that's left
  if (viewerDiv.childNodes.length == 1) {
    viewerDiv.removeChild(viewerDiv.firstChild);
    setInfoViewerHeight();
  }
}

// add a "Close All" button at the beginning of viewerDiv
function addCloseAll() {
  var html = "<table class='viewer'><tr><td class='viewerButton' width='100%'>"
    + "<input type='button' class='viewerButton' onclick='clearInfoViewer();' value='Close All'/></td></tr></table>";
  document.getElementById("viewerDiv").innerHTML = html + document.getElementById("viewerDiv").innerHTML;
}

// handler for the "Close All" button
function clearInfoViewer() {
  document.getElementById("viewerDiv").innerHTML = "";
  setCookie("infoViewer", "");
  setInfoViewerHeight();
}

// debugging
function alertInfoViewer() {
  var viewerDiv = document.getElementById("viewerDiv");
  alert(viewerDiv.innerHTML);
  var nodeInfo = "";
  for (var ix = 0; ix < viewerDiv.childNodes.length; ix ++) {
    nodeInfo += (viewerDiv.childNodes[ix].nodeName + " [" + viewerDiv.childNodes[ix].nodeValue + "]\n");
  }
  alert(nodeInfo);
}

