  var map;
  var map2;
  var directions = new google.maps.DirectionsService();
  var directionsDisplay = new google.maps.DirectionsRenderer({
    suppressMarkers: true,
    preserveViewport: true
  });
  var elevation = new google.maps.ElevationService();
  google.load("visualization", "1", {packages: ["columnchart"]});
  var directions_result;
  var panelDirections;
  var mapPanelContainer = [];
  var polygon_container = [];
  var marker_container = [];
  var ModeSpecificControls;
  var small_mode_control;
  var small_mode_drop_control;
  var mode_control;
  var LeftPanelButtons, RightPanelButtons;
  var clckTimeOut = null;
  var dragtimeout = null;
  var infowindow;
  var panelReDirectionsContainer = [];
  var travelMode = google.maps.DirectionsTravelMode.BICYCLING;
  var results_container = [];
  var polyline_container = [];
  var directionsQue = [];
  var mousemarker = null;
  var chart = null;

  function initializeMap()  {
    var latlng = new google.maps.LatLng(37.6533788818514, -97.7783175);
    var myOptions = {
      zoom: 4,
      center: latlng,
      mapTypeId: google.maps.MapTypeId.ROADMAP
    };
    map = new google.maps.Map(document.getElementById("map_canvas1"), myOptions);
    
    google.maps.event.addListener(map, 'click', function(event)  { 
      mapClick(event.latLng);
    });

    
  }
  
  function initializeUI()  {
    mode_control = new MapControl(map, "top", ModeButtonCreator(), google.maps.ControlPosition.RIGHT);    
    //resetButton = new MapControl(map, "top", startOverButtonCreator(), google.maps.ControlPosition.RIGHT);
    
    google.maps.event.addDomListener(document.getElementById("travel_mode_select"), "change", function()  {
	if (document.getElementById("travel_mode_select").selectedIndex)  {
	  //select driving if the index is 1.
	  travelMode = google.maps.DirectionsTravelMode.DRIVING;
	}
	else  {
	  //select bicycling if the index is 0.
	  travelMode = google.maps.DirectionsTravelMode.BICYCLING;
	}
	  
      });
    
    //Load the info window full of instructions and tips!
    var contentString = '<div style="font-size: 1em; height: 220px;">' + 
      '<h5>How To Create A Map:</h5>' +
      '<ol>' +
      '<li>Click on the map to create way-points.</li>' +
      '<li style="margin-top: 5px;">Divide the route into pages by panning and zooming to a viewpoint that provides a good level of detail. Then click ‘Add Page.’ Repeat until all of the route is represented in a page.</li>' +
      '<li style="margin-top: 5px;">Click the print button to move to print mode. Click the printer icon or user your browser’s print function to print the pages. You can save your route if you are <a href="http://www.ridefreebikemaps.com/members-only-map-creator">logged in</a>.</li>' +
      '</ol>' + 
      '<h5>Tips:</h5>' + 
      '<ul>' +
      '<li>Click and drag on waypoints to change their position</li>' +
      '<li>Clicking on waypoints will delete them.</li>' +
      '</ul>' +
      '</div>'
      
    
    
    
    //For some reason the controls take a bit to load so this is here to prevent calling setCurrentMode before
    //the mode controls have loaded.
    var load_interval = setInterval(function()  {
      if (document.getElementById("panel_mode_button"))  {
	  setCurrentMode("panel");
	
	latLngSpan = map.getBounds().toSpan();
	var infowindowlatlng = new google.maps.LatLng(map.getCenter().lat() - latLngSpan.lat() * .35, map.getCenter().lng() - latLngSpan.lng() * .2);
	
	infowindow = new google.maps.InfoWindow({
	  content: contentString,
	  position: infowindowlatlng 
	});
      
	infowindow.open(map);
	clearInterval(load_interval);
      }
    }, 200);
  }
  
  function findDistance(latLngA, latLngB)  {
    return Math.sqrt(Math.pow(latLngB.lat()-latLngA.lat(),2) + Math.pow(latLngB.lng()-latLngA.lng(),2));
  }

  function directionsLoaded() {
    results_container.push(directions_result);

    var pathOptions = {
      path: directionsResultLatLngs(directions_result),
      strokeColor: '#0000CC',
      strokeOpacity: 0.4,
      map: map
    };

    //Find where the polyline goes by calculating the distance between the first point in the polyline and 
    //the location of the markers. Shortest distance wins.
    var distances = [];
    var polyline_start_location = pathOptions.path[0];

    for(marker_num = 0; marker_num < marker_container.length; marker_num++)  {
      //Creates and array of arrays which contain distance[0] and marker index[1].
      distances.push([findDistance(polyline_start_location, marker_container[marker_num].getPosition()),marker_num]);
    }

    //sort the distances, the first element will be where the polyline should go.
    distances.sort(function(_a,_b)  {
	return _a[0] - _b[0];
      });
    polyline_container[distances[0][1]] = new google.maps.Polyline(pathOptions);
    polyline_container[distances[0][1]].distance = directions_result.routes[0].legs[0].distance.value / 1000;

    //manually center directions
    //map.fitBounds(directionsCenter(getVerticies(directions_result.routes[0])));
    //document.getElementById("distance").innerHTML = "Distance: " + Math.round(directions_result.routes[0].legs[0].distance.value / 1609.344) + " mi. (" + Math.round(directions_result.routes[0].legs[0].distance.value / 1000) + " km)";
    
    var distance_sum = 0;
    for (var x =0; x < polyline_container.length; x++)	{
      distance_sum += polyline_container[x].distance;
    }
    document.getElementById("distance").innerHTML = "Distance: " + Math.round(distance_sum / 1.609344) + " mi. (" + Math.round(distance_sum) + " km)";
  
    //Check to see if there are any directions waiting in the que.
    
    if (directionsQue.length)  {
      window.setTimeout(directionsQueProcessor(), 1000);
      //directionsQueProcessor();
    }
  }

  //Returns a MVCArray of latlngs from a directions result.
  function directionsResultLatLngs(result)  {
    var latlngs = [];
    //iterate through the steps of the result
    for (x = 0; x < result.routes[0].legs[0].steps.length; x++)  {
      //iterate through the latlngs of a step. The last latlng will the the first in the next step 
      //so it is left off.
      for (y = 0; y < result.routes[0].legs[0].steps[x].lat_lngs.length - 1; y++)  {
	latlngs.push(result.routes[0].legs[0].steps[x].lat_lngs[y]);
	latlngs[latlngs.length - 1].instructions = result.routes[0].legs[0].steps[x].instructions;
	latlngs[latlngs.length - 1].distance = result.routes[0].legs[0].steps[x].distance;
      }
      //Push the last step into the array because it was left off the last iteration of the loop. 
      latlngs.push(result.routes[0].legs[0].steps[x].lat_lngs[y]);
    }
    

    return latlngs;
  }

  //function called when map is clicked. Starts a timer which if uninterupted by anouther click executes the plotRoute function. 
  //If abotehr click occurs then the timer resets and a double click function can be run. At this time this is reserved for built in double click zoom.
  function mapClick(latLng)  {
    if (clckTimeOut)  {
      window.clearTimeout(clckTimeOut);
      clckTimeOut = null;
      //Enter doubleclick funtion here if needed.
    }
    else  {
      clckTimeOut = window.setTimeout(function()  {plotRoute(latLng)}, 275);
    }
  }
  
  
  
  function plotRoute(latLng) {
    var start_position, end_position;
    //reset double click detection.
    window.clearTimeout(clckTimeOut);
    clckTimeOut = null;
    //wass used to raise the max waypoints error.
  
    if (latLng)  {
      // Create a new marker based on the coordinates
      var marker = new google.maps.Marker({
	position: latLng,
	map: map,
	draggable: true
      });

      //Save the marker for funture uses, such as _REMOVAL_
      marker_container.push(marker);
      
      //Click the marker to remove it.       
      google.maps.event.addListener(marker, "click", function()  {
	marker_index = findIndex(marker_container, this);
	//Directions need to be recalculated if a marker is removed from the middle of the route.
	if (marker_index != 0 && marker_index < marker_container.length - 1)  {
	  start_position = marker_container[marker_index-1];
	  end_position = marker_container[marker_index+1];
	}
	
	//remove the marker from the map and the container
	marker_container.splice(marker_index, 1);
	this.setMap(null);	

	//remove the polyline from the map and the container
	if (polyline_container.length)  {
	  //If deleting the last marker then remove the last polyline. 
	  if (marker_index == marker_container.length)  {
	    polyline = polyline_container.splice(-1,1);
	    polyline[0].setMap(null);
	  }

	  else if (marker_index == 0)  {
	    polyline = polyline_container.splice(marker_index,1);
	    polyline[0].setMap(null);
	  }

	  //if not removing the first or last marker then directions need to be recalculated. 
	  else  {
	    polyline = polyline_container.splice(marker_index,1);
	    polyline[0].setMap(null);

	    //Need to clear the polyline before it can be overwritten in the container.		
	    polyline_container[marker_index - 1].setMap(null);
	    //find directions with start and end points.
	    directionsQueProcessor(start_position, end_position);
	    
	  }
	}
      }); 
      //add the dragend event to the new marker. 
      google.maps.event.addListener(marker, "dragend", function(event)  {
	if (marker_container.length > 0)  {  //if there is only on marker do nothing. 
	  marker_index = findIndex(marker_container, marker);
	  //splice out the first polyline and find the directions. 
	  if (marker_index == 0)  {
	    polyline = polyline_container.splice(0,1);
	    polyline[0].setMap(null);
	    directionsQueProcessor(marker_container[0], marker_container[marker_index + 1]);
	  }
	  //splice out the last polyline and find the new directions.
	  else if (marker_index == marker_container.length - 1)  {
	    polyline = polyline_container.splice(-1,1);
	    polyline[0].setMap(null);
	    directionsQueProcessor(marker_container[marker_index - 1], marker_container[marker_index]);
	  }
	  //If the marker is in the middle then the polyline_container should be altered. Just set the map to
	  //null of the right lines and overwrite them when the directions are loaded. 
	  else  {
	    polyline_container[marker_index - 1].setMap(null);
	    polyline_container[marker_index].setMap(null);
	    directionsQueProcessor(marker_container[marker_index - 1], marker_container[marker_index]);
	    directionsQueProcessor(marker_container[marker_index], marker_container[marker_index + 1]);
	  }
	  //Re-do the directions associated with each page (just in case they changed).
	  if (mapPanelContainer.length > 0)  {
	    for (x = 0; x < mapPanelContainer.length; x++)  {
	      mapPanelContainer[x].findPanelDirections(map);
	    }
	  }
	}
      });
      //Find directions if there are more than one marker.
      numMarkers = marker_container.length;
      if (numMarkers > 1)  {
	//Calls the directionsQueProcessor with the markers to increase accuravy of directions.
	directionsQueProcessor(marker_container[numMarkers-2],marker_container[numMarkers-1])
      }
    }
  }
  
  function directionsQueProcessor()  {
    //Add items to the que if supplied.
    if (arguments.length)  {
      directionsQue.push(arguments);
    

      //Process items in que.
      if (directionsQue.length > 0)  {
	request = directionsQue.shift();
	//test for null.
	if (request)  {
	  //request is start and end marker.
	  findDirections(request[0],request[1]);
	}
	//if the request is null then execute w/o markers.
	else  {
	  findDirections();
	}
      }
    }
    else  {
      findDirections();
    }
  }

  //Find the directions between a set of waypoints. 
  function findDirections()	{
    var directions_request;  
    window._fd = window._fd + 1;
    //If start and end points are provided to the function use those.
    if (arguments.length > 1)  {
      start_point = arguments[0].getPosition();
      end_point = arguments[1].getPosition();
      
      
    }
    //else use the location of the last two markers. Happens after a click on the map.
    else  {
      start_point = marker_container[marker_container.length - 2].getPosition();
      end_point = marker_container[marker_container.length -1].getPosition();
    }
  
	
      
    directions_request = {
      origin: start_point,
      destination: end_point,
      travelMode: travelMode,
      avoidHighways: true//,
      //waypoints: waypts
    };
    
    directions.route(directions_request, function(result, status)  {
      if (status == "OK")  {	
	directions_result = result;
	directionsLoaded();
      }
      else if (status == "UNKNOWN_ERROR")  {
	//It's safe to retry directions with this error.
	for (t = 0; polyline_container[t] != undefined && t < polyline_container.length -1; t++)  {
	  //Nothing to do in here. Just need t when the loop exits.Looking for undefined polylines.
	}
	//If the loop has found an undefined polyline then re-do the directions for it. 
	if (marker_container[t] == undefined)  {
	  directionsQueProcessor(marker_container[t], marker_container[t+1]);
	}
	//When there are no undefined polylines then just recalc the directions using the last two markers.
	else   {
	  directionsQueProcessor(marker_container[marker_container.length-2],marker_container[marker_container.length-1]);
	}
      }
      else  {
	alert("The last waypoint could not be added to the map.");
	m = marker_container.pop();
	m.setMap(null);
      }
    });

    google.maps.event.trigger(map, 'resize');
    
    document.getElementById("instructions").innerHTML = "Create panels by clicking the <b>+ Panel</b> button.";
    if (document.getElementById("print_mode_button") && document.getElementById("print_mode_button").style.fontWeight == "bold")  {
      setCurrentMode("panel");
    }
  }
  
  
  //function to alter the info window of a google bar produced marker. Adds an 'add waypoint' link.
  function GoogleBarInfoWinMod(marker, html, result)  {
    createMarkerLink = document.createElement("a");
    createMarkerLink.appendChild(document.createTextNode("Add Waypoint."));
    html.appendChild(createMarkerLink);
    GEvent.addDomListener(createMarkerLink, "click", function() {GoogleBarAddMarker(marker);});
    return html;
  }
  
  function GoogleBarAddMarker(marker)  {
    newMarker = new GMarker(marker.getLatLng(), {"draggable":true});
    map.addOverlay(newMarker);
    marker_container.push(newMarker);
    GEvent.addDomListener(newMarker, "click", function()  {
      map.removeOverlay(this);
      var x = 0;
      while (x < marker_container.length)  {
	if (marker_container[x] == this)  {
	  marker_container.splice(x, 1);
	  break;
	}
	x++;
      }
    });
  }
  
  
  //Sets the button font to bold and changes the button border color.
  function setCurrentMode(mode)  {
    if (!document.getElementById)  {
      window.setTimeout(setCurrentMode(mode), 100);
    }
    var button = document.getElementById(mode + "_mode_button");
    var button_container = [];
    
    //get all the buttons in one array
    /*button_container.push(document.getElementById("plot_mode_button"));*/
    button_container.push(document.getElementById("panel_mode_button"));
    button_container.push(document.getElementById("print_mode_button"));
    
    //loop through the array and set the new mode all else gets set to the default appearence. 
    for (var x = 0; x < button_container.length; x++)  {
      if (button_container[x] != button)  {
	button_container[x].style.fontWeight = "normal";
	button_container[x].style.borderColor = "white rgb(176, 176, 176) rgb(176, 176, 176) white";
      }
      else  {
	button.style.fontWeight = "bold";
	button.style.borderColor = "rgb(52, 86, 132) rgb(108, 157, 223) rgb(108, 157, 223) rgb(52, 86, 132)";
      }
    }
    
    //Remove the extra print mode maps.
    static_maps = document.getElementById("static_map_box");
    nodes = static_maps.childNodes;
    
    for(x = nodes.length - 1; x >= 0; x--)  {
      static_maps.removeChild(nodes[x]);
    }
    
    //Show/hide specific mode controls
    if (map.controls[google.maps.ControlPosition.RIGHT].getLength() > 1)  {
      map.controls[google.maps.ControlPosition.RIGHT].removeAt(1);
    }
    
    if (mode == "plot")  {
      ModeSpecificControls = new MapControl(map, "top", PlotButtonCreator(), google.maps.ControlPosition.RIGHT); 
    }
    
    if (mode == "panel")  {      
      ModeSpecificControls = new MapControl(map, "top", PanelButtonCreator(), google.maps.ControlPosition.RIGHT);
    }
  }


  //This clears all overlays on the map, text directions, and the list of waypoints associated with
  //the directions. 
  function clearMarkers()  {
    //Clear markers
    for (x = 0; x < marker_container.length; x++)  {
      marker_container[x].setMap(null);
    }
    
    //Clear Polylines
    for (x = 0; x < polyline_container.length; x++)  {
      polyline_container[x].setMap(null);
    }
    
    //Clear the saved panels
    clearPanels();
    
    
    directionsDisplay.setMap(null);
    
    //Clear Globals
    marker_container = [];
    polyline_container = [];
    results_container = [];

    //Clear text directions
    document.getElementById("directionspanel").innerHTML = "";
    
    //Change the size and appearence of the map
    setCurrentMode("panel");
  }

  function plotMode()  {   
    //Change the size and appearence of the map
    document.getElementById("text_directions").style.display = "none";    
    document.getElementById("map_canvas1").style.width = "100%";
    google.maps.event.trigger(map, 'resize');
    document.getElementById("instructions").innerHTML = "Click on the map to add way-points. To divide the route into sections click <b>Add Page</b>.";
    document.getElementById("plot_mode_button");    
    
    //Reset the sidebar, regardless if needed.
    parent.document.getElementById("sidebar").style.display = "block";
    parent.document.getElementById("container").style.width = "784px";
    parent.document.getElementById("container").style.marginLeft = "240px";
  }

  //This function creates a link with enough information in the url to recreate a map viewpoint. This 
  //also creates a GPolygon around each viewpoint so it is easy to keep track of them. This function 
  //should be rewritten to take advantage of the mapPanel object (also expand the functionality of 
  //the mapPanel object to encompass everything this function does.
  function addMapPanel()	{
    
    var mappanel = new mapPanel(map);
    mapPanelContainer.push(mappanel);
    
    //Finding which panel the new is.
    var p = mapPanelContainer.length;
    
    //Create a box around the viewpoint to make keeping track of panels easy.
    mappanel.createPolygon();
    
    //Write the directions to the text area, will not be shown untill print mode is selected. 
    mappanel.findPanelDirections(map);
    
    
    var panel_buttons_div = document.getElementById("Left_Panel_Buttons");
    if (panel_buttons_div)  {
      //This call to MapControl is a dummy call almost. Only the child of the return value is needed.
      NewPanelButton = new MapControl(map, "drop", PanelRecallButtonCreator(p, map), google.maps.ControlPosition.BOTTOM_LEFT);
      panel_buttons_div.appendChild(NewPanelButton.firstChild);
    }
    else  {
      LeftPanelButtons = new MapControl(map, "drop", PanelRecallButtonCreator(p, map), google.maps.ControlPosition.BOTTOM_LEFT);
      LeftPanelButtons.setAttribute("id", "Left_Panel_Buttons");
    }


  }
  
  //Reset the panel management links
  function clearPanels()	{
    //map.controls[google.maps.ControlPosition.BOTTOM_LEFT].unbindAll();
    panel_buttons = document.getElementById("Left_Panel_Buttons");
    if (panel_buttons)  {
      panel_buttons.parentNode.removeChild(panel_buttons);
    }
    
    mapPanelContainer = [];
    
    for (x in polygon_container)	{
      if (polygon_container[x].map)  {
	polygon_container[x].setMap(null);	
      }
    }
    polygon_container = [];
  }
 
  
  //Recall a map viewpoint from a link created in the addMapPanel function.
  function recallPanel(p, panelMap)	{
    mapPanelContainer[p-1].recallMapPanel();
    mapPanelContainer[p-1].showPanelDirections();
  }
  
  //Show the written directions for only the portion of the route that is visible in a map panel.
  //This can use the mapPanel object.
  function showPanelDirections ()	{	
    var panel_directions = document.createElement("div");
       
    //clear current directions
    panel_directions.innerHTML = "|0|";
    paneldistance = 0;
    
    //Get all the routes in a panel.
    directionslegs = [];
    for (i = 0; i < panelDirections.routes[0].legs.length; i++)  {
      directionslegs.push(panelDirections.routes[0].legs[i]);
    }
    
    //loop through each element in the directionsroutes which is a route
    for (x in directionslegs)	{
      //loop through the steps in the current route
      for (var y = 0; y < directionslegs[x].steps.length; y++)	{
	step = directionslegs[x].steps[y];
	if (this.bounds.contains(step.start_point))	{
	  //gets the distance of a step and adds it to the running total of all steps visible in the panel.
	  paneldistance += (step.distance.value * 0.000621);
	  if (paneldistance >= 10)	{
	    precision = 3;
	    }
	  else {
	    precision = 2;
	    }
	  //This is supposed to show the distance from the start of the panel to a given step in the 
	  //directions. It is not doing that. Instead it is showing the distance from the first step 
	  //in the panel.
	  panel_directions.innerHTML += step.instructions + "<br>|" + paneldistance.toPrecision(precision) + "| " ;
	  }
	}
      }
      
      //document.getElementById("directionspanel") used to display the directions while creating the pages. I decided not to show directions 
      //until printing so now it's pretty useless. I'm keeping this in here just to show what I did. I think I may want to go back someday.
      //panel_directions.innerHTML = document.getElementById("directionspanel").innerHTML;
      panel_directions.setAttribute("class", "main_print_content");
      panel_directions.style.width = "280px";
      panel_directions.style.fontSize = "small";
      this.panelDirectionsHTML = panel_directions;
      this.panelDirections = panelDirections;
    }
    
  function panelDirectionsLoaded(result, status)  {
    panelDirections = result;
    //Need to call showPanelDirections via a mapPanel object. The last one created should be the correct one to call from.
    var mapPanel = panelReDirectionsContainer.shift();
    mapPanel.showPanelDirections();
  }
  
  //This is the template for mapPanel objects. Each mapPanel will have a center, zoom level, bounds, and
  //splitstate properties. In addition each mapPanel will have a few methods for editing and recalling 
  //the panel. This object is initialized by passing it a GMap2 object. Additionaly this object could have
  //directions and distances associated with it. 
  function mapPanel (panelmap)	{
    //properties
    this.center = panelmap.getCenter();
    this.zoom = panelmap.getZoom();
    this.bounds = panelmap.getBounds();
    
    //methods
    this.recallMapPanel = recallMapPanel;
    this.showPanelDirections = showPanelDirections;
    this.createPolygon = createPolygon;
    this.findPanelDirections = findPanelDirections;
  }

  //Part of the mapPanel object
  function recallMapPanel(panelMap)	{
    map.setCenter(this.center);
    map.setZoom(this.zoom);
    //this.findPanelDirections(panelMap);
    document.getElementById("directionspanel").innerHTML = this.panelDirectionsHTML;
  }
  
  //This function takes a directions request result route and returns an array filled with all the verticies of the 
  //ployline of the request.
  function getVerticies(result_route)  {
    var verticies = [];
    //Loop through the legs.
    for (var a = 0; a < result_route.legs.length; a++)  {
      //loop through the setps
      for (var b = 0; b < result_route.legs[a].steps.length; b++)  {
	//loop through the LatLngs
	for (var c = 0; c < result_route.legs[a].steps[b].lat_lngs.length; c++)  {
	  verticies.push(result_route.legs[a].steps[b].lat_lngs[c]);
	}
      }
    }
    
    return verticies;
  }
  
  //Part of the mapPanel Object.
  function findPanelDirections()  {    
    var verticies = [];
    var entrance;
    var exit;
    var region = "";
    var panelWaypoints = [];
    
    for (path = 0; path < polyline_container.length; path++)  {
      latLngs = polyline_container[path].getPath();
      for (x = 0; x < latLngs.length; x++)  {
	verticies.push(latLngs.getAt(x));
      }
    }

    for (x = 0; x < marker_container.length; x++)  {
      if (this.bounds.contains(marker_container[x].getPosition()))  {
	//Testing if the bounds contains the start/end point.
	if (x === 0)  {
	  entrance = marker_container[x].getPosition();
	}
	if (x == marker_container.length - 1)  {
	  exit = marker_container[x].getPosition();
	}
	panelWaypoints.push(marker_container[x]);
      }
    }
    
    i = 0;
    while (!entrance || !exit)	{
      if(this.bounds.contains(verticies[i]) && !entrance)	{
	entrance = verticies[i]
	region = outOfBoundsTest(this.bounds, entrance, verticies[i-1]);
	var intersect = intersectionSolver(this.bounds, entrance, verticies[i-1], region);
	panelWaypoints.splice(0,0,intersect);
      }
      if (!this.bounds.contains(verticies[i]) && entrance)	{
	exit = verticies[i];
	region = outOfBoundsTest(this.bounds, verticies[i-1], exit);
	var intersect = intersectionSolver(this.bounds, verticies[i-1], exit, region);
	panelWaypoints.push(intersect);
      }
      i++;

      //If a page is created  w/o having the route through it just exit the function.
      if (i > verticies.length)  {
	return;
      }
    }
    
    waypts = []
    
    //construct the waypoints array, start at 1 and end prior to the last to preserve start/end.
    for (x = 1; x < panelWaypoints.length -1; x++)  {
      waypts.push({
	location: panelWaypoints[x].getPosition(),
	stopover: false
      });
    }
    
    
    //Define panelDirections
    panelDirectionsOpts = {
	origin: entrance,
	destination: exit,
	travelMode: travelMode,
	avoidHighways: true,
	waypoints: waypts
    };
    panelReDirectionsContainer.push(this);
    directions.route(panelDirectionsOpts, panelDirectionsLoaded);    
  }
 
  
  function createPolygon()  {
    
    //turning the bounds into a set of 5 verticies to create a rectangular GPolygon.
    /*var ne = this.bounds.getNorthEast();
    var sw = this.bounds.getSouthWest();

    var nw = new google.maps.LatLng(sw.lat(), ne.lng());
    var se = new google.maps.LatLng(ne.lat(), sw.lng());  */
    
    var rectangleOpts = {
      bounds: this.bounds,
      map: map,
      strokeColor: "#f33f00",
      strokeOpacity: .5,
      strokeWeight: 3,
      fillColor: "#ff0000",
      fillOpacity: 0.04
    }
    
    var panelbox = new google.maps.Rectangle(rectangleOpts);
    polygon_container.push(panelbox);
  }
  
  //Put the app and the page into print mode.
  function printMode(state)	{
    if (state)	{
      
      //hide all the polygons on the map.
      for (x in polygon_container)	{
	polygon_container[x].setMap(null);
      }
      
      //adjust the width of the app by removing unecessary navigation.
      parent.document.getElementById("sidebar").style.display = "none";
      parent.document.getElementById("container").style.width = "100%";
      parent.document.getElementById("container").style.marginLeft = "0px";

      //document.getElementById("map_canvas1").style.width = "611px";
      //google.maps.event.trigger(map, 'resize');//}
      
      document.getElementById("text_directions").style.display = "inline-block";
      
      //Clear text directions
      document.getElementById("directionspanel").innerHTML = "";	
      
      document.getElementById("instructions").innerHTML = "<span>Print your map and go for a ride!</span><a href=\"javascript:window.print()\"><img src=\"/print-printer-icon.png\" style=\"float: right; border: none\"></a>";
      setCurrentMode("print");

      //Elevation code
      var verticies = [];

      for (path = 0; path < polyline_container.length; path++)  {
	latLngs = polyline_container[path].getPath();
	for (x = 0; x < latLngs.length; x++)  {
	  verticies.push(latLngs.getAt(x));
	}
      }

      //If the array length is too large it will need to be shortened. Use the short value
      //in a for loop to selectively splice the array.
      if (verticies.length > 385)  {
	_short = verticies.length / 270;
	old_verticies = verticies;
	verticies = [];
	for (x = 0; x < old_verticies.length; x += _short)  {
	  if (old_verticies[Math.round(x)] != undefined)  {
	    verticies.push(old_verticies[Math.round(x)]);
	  }
	}
      }

      /*encodedPolylinesConstructor = new PolylineEncoder();
      encodedPolylines = encodedPolylinesConstructor.dpEncodeToJSON(verticies);*/
      chart = new google.visualization.ColumnChart(document.getElementById('directionspanel'));
      if (polyline_container.length > 0)  {
	var path_request = {
	  'path': verticies,
	  'samples': 500
	}
	
	google.visualization.events.addListener(chart, 'onmouseover', function(e) {
	if (mousemarker == null) {
	  mousemarker = new google.maps.Marker({
	    position: elevations[e.row].location,
	    map: map,
	    icon: "http://maps.google.com/mapfiles/ms/icons/green-dot.png"
	  });
	} else {
	  mousemarker.setPosition(elevations[e.row].location);
	}
      });
	  
	// Initiate the path request.
	if (elevation) {
	  elevation.getElevationAlongPath(path_request, plotElevation);
	}
      }
      
    }
    else	{
      for (x in polygon_container)	{
	polygon_container[x].setMap(map);
      }
      parent.document.getElementById("sidebar").style.display = "block";
      parent.document.getElementById("container").style.width = "784px";
      parent.document.getElementById("container").style.marginLeft = "240px";
      document.getElementById("text_directions").style.display = "none";
      //document.getElementById("map_canvas1").style.width = "611px";
      google.maps.event.trigger(map, 'resize');
      setCurrentMode("panel");
      document.getElementById("instructions").innerHTML = "Create panels by clicking the <b>Add Page</b> button.";
      
    }
  }
  // Remove the green rollover marker when the mouse leaves the chart. For elevation shart
  function clearMouseMarker() {
    if (mousemarker != null) {
      mousemarker.setMap(null);
      mousemarker = null;
    }
  }
  
  function plotElevation(results, status)  {
    if (status == google.maps.ElevationStatus.OK) {
      elevations = results;

      // Extract the elevation samples from the returned results
      // and store them in an array of LatLngs.
      /*var elevationPath = [];
      for (var i = 0; i < results.length; i++) {
	elevationPath.push(elevations[i].location);
      }

      // Display a polyline of the elevation path.
      var pathOptions = {
	path: elevationPath,
	strokeColor: '#0000CC',
	opacity: 0.4,
	map: map
      }
      polyline = new google.maps.Polyline(pathOptions);*/

      // Extract the data from which to populate the chart.
      // Because the samples are equidistant, the 'Sample'
      // column here does double duty as distance along the
      // X axis.
      var data = new google.visualization.DataTable();
      data.addColumn('string', 'Sample');
      data.addColumn('number', 'Elevation');
      for (var i = 0; i < results.length; i++) {
	data.addRow(['', elevations[i].elevation * 3.2808399]);  //convert elevation in meters to feet. 
      }

      // Draw the chart using the data within its DIV.
      document.getElementById('directionspanel').style.display = 'block';
      chart.draw(data, {
	width: 280,
	height: 200,
	legend: 'none',
	titleY: 'Elevation (ft.)'
      });
    }
  }
  
  //Test where out of bounds a point is. The arguments should be a LatLngBounds and LatLng.
  function outOfBoundsTest(bounds, pointin, pointout)	{
    var ne = bounds.getNorthEast();
    var sw = bounds.getSouthWest();
    var top = ne.lat();
    var bottom = sw.lat();
    var right = ne.lng();
    var left = sw.lng();
    var region;
    
    
    do	{
      region = "";
      //Determine where the point is out of bounds. 
      if (pointout.lat() > top)  {
	region += "top";
      }
      if (pointout.lat() < bottom) {
	region += "bottom";
      }
      if (pointout.lng() > right)  {
	region += "right";
      }
      if (pointout.lng() < left) {
	region += "left";
      }
      pointout = new google.maps.LatLng(.1 * (pointin.lat()-pointout.lat()) + pointout.lat(), (.1 * (pointin.lng() - pointout.lng()) + pointout.lng()));
      
    }
    while (region.length > 6);
    return region;
  }
    
  function intersectionSolver(bounds, pointin, pointout, region)  {
    var ne = bounds.getNorthEast();
    var sw = bounds.getSouthWest();
    var top = ne.lat();
    var bottom = sw.lat();
    var right = ne.lng();
    var left = sw.lng();

    var slope = ((pointin.lat()-pointout.lat())/(pointin.lng()-pointout.lng()));
    var intercept = pointout.lat() - (slope * pointout.lng());
    
    switch(region)  {
      case "top":
	var intersect = new google.maps.LatLng(top,((top-intercept) / slope));
	if (Math.abs(slope) == Infinity)  {
	  intersect = new google.maps.LatLng(top,pointin.lng());
	}
	break;
      case "bottom":
	intersect = new google.maps.LatLng(bottom,((bottom-intercept) / slope));
	if (Math.abs(slope) == Infinity)  {
	  var intersect = new google.maps.LatLng(bottom,pointin.lng());
	}
	break;
      case "left":
	intersect = new google.maps.LatLng(((slope * left) + intercept), left);
	break;
      case "right":
	intersect = new google.maps.LatLng(((slope * right) + intercept), right);
	break;
    }
    return intersect;
  }
  
  
  
  
  //Simple constructor for button objects. The name is the text displayed. The action is the function preformed. The id is and
  //optional parameter used if the button needs an element id.
  function button(buttonName, buttonAction, buttonId)  {
    this.name = buttonName;
    this.id = buttonId;
    this.action = buttonAction;
  }
  
  
  /******************************************
  //Constructs the button groups. Horizontal groups are called top. Single buttons with drop down style menus are called drop.
  //The actual buttons themselves should be passed as an array of button types (see above). If 'drop' is specified the first in the 
  //array will serve as the title for the drop menu.
  ******************************************/
  function MapControl(map, controlType, buttons, position)  {
    var container = document.createElement("div");
    var middle, inner;
    
    switch (controlType)  {
      case "top":	
	for (var x = 0; x < buttons.length; x++)  {
	  //Create the structure of the buttons.	  
	  inner = document.createElement("div");
	  inner.appendChild(document.createTextNode(buttons[x].name));
	  if (buttons[x].id)  {
	    inner.setAttribute("id", buttons[x].id);
	  }
	  
	  middle = document.createElement("div");
	  middle.appendChild(inner);
	  
	  container.appendChild(middle);
	  //set the styles
	  this.setTopInnerStyle(inner);
	  this.setTopMiddleStyle(middle);
	  
	  
	  //set the action of clicking the button
	  google.maps.event.addDomListener(middle, "click", buttons[x].action);
	}
	this.setTopContainerStyle(container);
	
	break;
	
      case "drop":
	for (var x = 0; x < buttons.length; x++)  {
	  //This sets up the buttons in a drop menu.
	  inner = document.createElement("div");
	  inner.appendChild(document.createTextNode(buttons[x].name));
	  if (buttons[x].id)  {
	    inner.setAttribute("id", buttons[x].id);
	  }
	  container.appendChild(inner);
	  
	  //Set styles
	  this.setDropInnerStyle(inner);
	  
	  //set the actions
	  google.maps.event.addDomListener(inner, "click", buttons[x].action);
	}
	this.setDropContainerStyle(container);
	break;
    }
    container.index = 1;
    map.controls[position].push(container);
    return container;
  }
  
  MapControl.prototype.setTopInnerStyle = function(inner)  {
    inner.style.border = "1px solid";
    inner.style.borderColor = "white rgb(176, 176, 176) rgb(176, 176, 176) white";
    inner.style.font = "12px Arial";
    inner.style.width = "63px";
  };
  
  MapControl.prototype.setTopMiddleStyle = function(middle)  {
    middle.style.border = "1px solid black";
    middle.style.cursor = "pointer";
    middle.style.textAlign = "center";
    middle.style.display = "inline-block";
  };
  
  MapControl.prototype.setTopContainerStyle = function(container)  {
    container.style.backgroundColor = "white";
    container.style.color = "black";
    container.style.height = "18px";  
    container.style.marginRight = "5px";
    container.style.marginTop = "5px";
    container.className = "dont_pri";
  };
  
  MapControl.prototype.setDropContainerStyle = function(container)  {
    container.style.color = "black";
    container.style.backgroundColor = "white";
    container.style.border = "1px solid black";
    container.className = "dont_pri";
  };
    
  MapControl.prototype.setDropInnerStyle = function(inner)  {
    inner.style.font = "12px Arial";
    inner.style.width = "59px";
    inner.style.cursor = "pointer";
    inner.style.paddingLeft = "6px";
  };
  
  function ModeButtonCreator()  {
    var buttons = [];
    /*buttons.push(new button("Route", function() {plotMode();setCurrentMode("plot");}, "plot_mode_button")); */
    buttons.push(new button("Divide", function() {printMode(false);}, "panel_mode_button"));
    buttons.push(new button("Print", function() {
      printMode(true);
      //Recenter/zoom map to an oviewview viewport.
      var marker_positions = [];
      for (x = 0; x < marker_container.length; x++)  {
	marker_positions.push(marker_container[x].getPosition());
      }
      map.fitBounds(directionsCenter(marker_positions));

      for(i = 0; i < mapPanelContainer.length; i++)  {
	createStaticMap(mapPanelContainer[i]);
      }
    }, "print_mode_button"));
    buttons.push(new button("Start Over", function ()  {clearMarkers();}));
    return buttons;
  }  
  
  function createStaticMap(mapPanel)  {
    
    static_map = document.createElement("div");
    static_map.style.height = "480px";
    static_map.style.width = "611px";
    static_map.style.cssFloat = "left";
    static_map.style.styleFloat = "left";
    static_map.style.margin = "5px";
      
    static_map.setAttribute("class", "main_print_content");
    container = document.createElement("div");
    container.appendChild(static_map);
    
    container.setAttribute("class", "pages");
    
    document.getElementById("static_map_box").appendChild(container);
    
    var page_map_opts = {
      zoom: mapPanel.zoom,
      center: mapPanel.center,
      mapTypeId: google.maps.MapTypeId.ROADMAP,
      disableDefaultUI: true,
      draggable: false,
      scrollwheel: false,
      mapTypeControl: true,
      disableDoubleClickZoom: true
    };
    page_map = new google.maps.Map(static_map, page_map_opts);

    //Break execution if the map does not contain any part of the route. Casue sometimes ya 
    //just want a map. 
    try  {
      mapPanel.panelDirectionsHTML.style.cssFloat = "left";
      mapPanel.panelDirectionsHTML.style.styleFloat = "left";
      container.appendChild(mapPanel.panelDirectionsHTML);

      var pathOptions = {
	path: directionsResultLatLngs(directions_result),
	strokeColor: '#0000CC',
	strokeOpacity: 0.4,
	map: map
      };

      page_map_renderer_opts = {
	directions: mapPanel.panelDirections,
	map: page_map,
	preserveViewport: true,
	suppressMarkers: true,
	polylineOptions: pathOptions
      };
      page_map_renderer = new google.maps.DirectionsRenderer(page_map_renderer_opts);  
    }
    catch(TypeError)  {
      
    }  
  
  }
  
  function PlotButtonCreator()  {
    var buttons = [];
    buttons.push(new button("Plot Route", function() {directionsQueProcessor();}));    
    return buttons;
  }
  
  function PanelButtonCreator()  {
      var buttons = [];
      buttons.push(new button("Add Page", function() {addMapPanel();}));
      //buttons.push(new button("Start Over", function ()  {clearMarkers();}));
      
      return buttons;
  }
  
  function startOverButtonCreator()  {
    var buttons = [];
    buttons.push(new button("Start Over", function ()  {clearMarkers();}));
    
    return buttons;
  }
  
  function SmallModeDropAction()  {
      if (small_mode_control === undefined)  {
	var splitButtons = [];
	splitButtons.push(new button("Plot", function()  {
	  plotMode();
	  setCurrentMode("plot");
	  map.removeControl(small_mode_control);
	  small_mode_control = undefined;
	}, "plot_mode_button"));
	splitButtons.push(new button("Panel", function() {printMode(false);map.removeControl(small_mode_control);small_mode_control = undefined;}, "panel_mode_button"));
	splitButtons.push(new button("Print", function() {printMode(true);map.removeControl(small_mode_control);small_mode_control = undefined;}, "print_mode_button"));
	
	small_mode_control = new MapControl(map, "drop", splitButtons, ["G_ANCHOR_TOP_LEFT", new GSize(75,24)]);
	map.addControl(small_mode_control);
      }
      else  {
	map.removeControl(small_mode_control);
	small_mode_control = undefined;
      }
    }
    
  function PanelRecallButtonCreator(panel_number, panelMap)  {
    var buttons = [];
    buttons.push(new button("Panel " + panel_number, function()  { recallPanel(panel_number, panelMap); }));
    return buttons;
  }
  
  //Use this to find a bounds appropriate for viewing the entire route. Pass it an array of verticies. Returns a LatLngBounds.
  function directionsCenter(verticies)  {
    var newBounds = new google.maps.LatLngBounds()
    for (bound = 0; bound < verticies.length; bound++)  {
      newBounds.extend(verticies[bound]);
    }
    
    return newBounds;
  }
  
  function findIndex(array, object)  {
    x = 0;
    while (x < array.length)  {
	    if (array[x] == object)  {
	      return x;
	    }
	    x++;
	  }
	return -1;
  }

  
