function customPanel(map, mapname, dirn, div) {
    var html = "";
  
    // ===== local functions =====

    // === waypoint banner ===
    function waypoint(point, type, address) {
      var target = '"' + mapname+".showMapBlowup(new GLatLng("+point.toUrlValue(6)+"))"  +'"';
      html += '<table style="border: 1px solid silver; margin: 10px 0px; background-color: rgb(238, 238, 238); border-collapse: collapse; color: rgb(0, 0, 0);">';
      html += '  <tr style="cursor: pointer;" onclick='+target+'>';
      html += '    <td style="padding: 4px 15px 0px 5px; vertical-align: middle; width: 20px;">';
      html += '      <img src="http://www.google.com/intl/en_ALL/mapfiles/icon-dd-' +type+ '-trans.png">'
      html += '    </td>';
      html += '    <td style="vertical-align: middle; width: 100%;">';
      html +=        address;
      html += '    </td>';
      html += '  </tr>';
      html += '</table>';
    }

    // === route distance ===
    function routeDistance(dist) {
      html += '<div style="text-align: right; padding-bottom: 0.3em;">' + dist + '</div>';
    }      

    // === step detail ===
    function detail(point, num, description, dist) {
      var target = '"' + mapname+".showMapBlowup(new GLatLng("+point.toUrlValue(6)+"))"  +'"';
      html += '<table style="margin: 0px; padding: 0px; border-collapse: collapse;">';
      html += '  <tr style="cursor: pointer;" onclick='+target+'>';
      html += '    <td style="border-top: 1px solid rgb(205, 205, 205); margin: 0px; padding: 0.3em 3px; vertical-align: top; text-align: right;">';
      html += '      <a href="javascript:void(0)"> '+num+'. </a>';
      html += '    </td>';
      html += '    <td style="border-top: 1px solid rgb(205, 205, 205); margin: 0px; padding: 0.3em 3px; vertical-align: top; width: 100%;">';
      html +=        description;
      html += '    </td>';
      html += '    <td style="border-top: 1px solid rgb(205, 205, 205); margin: 0px; padding: 0.3em 3px 0.3em 0.5em; vertical-align: top; text-align: right;">';
      html +=        dist;
      html += '    </td>';
      html += '  </tr>';
      html += '</table>';
    }

    // === Copyright tag ===
    function copyright(text) {
      html += '<div style="font-size: 0.86em;">' + text + "</div>";
    }
    

    // === read through the GRoutes and GSteps ===
    for (var i=0; i<dirn.getNumRoutes(); i++) {
      if (i==0) {
        var type="play";
      } else {
        var type="pause";
      }
      var route = dirn.getRoute(i);
      var geocode = route.getStartGeocode();
      var point = route.getStep(0).getLatLng();
      // === Waypoint at the start of each GRoute
      waypoint(point, type, geocode.address);
      routeDistance(route.getDistance().html+" (about "+route.getDuration().html+")");

      for (var j=0; j<route.getNumSteps(); j++) {
        var step = route.getStep(j);
        // === detail lines for each step ===
        detail(step.getLatLng(), j+1, step.getDescriptionHtml(), step.getDistance().html);
      }
    }

    // === the final destination waypoint ===   
    var geocode = route.getEndGeocode();
    var point = route.getEndLatLng();
    waypoint(point, "stop", geocode.address);

    // === the copyright text ===
    copyright(dirn.getCopyrightsHtml());

    // === drop the whole thing into the target div
    div.innerHTML = '<h3>Driving Directions</h3>' + html;
}

function displayDirections(str_from, str_to) {
	document.getElementById("driving_directions").style.display = 'none';

	var dirn = new GDirections(map);

	GEvent.addListener(dirn, "error", function() {
		alert("Directions Failed: " + dirn.getStatus().code);
	});

	dirn.load("from: " + str_from + " to: " + str_to, {getSteps:true});

	GEvent.addListener(dirn, "load", function() {
		customPanel(map, "map", dirn, document.getElementById("driving_directions"));

		dirn = null;
	});

	document.getElementById("driving_directions").style.display = 'block';
}

function getDirections() {
	var tf = document.forms['directions_form'];

	if (tf.elements['zip'].value != '') {
		if (tf.elements['dir_type'][0].checked) {
			from_address = gym_address;
			to_address = tf.elements['street'].value + ' ' + tf.elements['city'].value + ' ' + tf.elements['state'].value + ' ' + tf.elements['zip'].value;
		} else {
			from_address = tf.elements['street'].value + ' ' + tf.elements['city'].value + ' ' + tf.elements['state'].value + ' ' + tf.elements['zip'].value;
			to_address = gym_address;
		}
	
		displayDirections(from_address, to_address);
	} else {
		alert('You must provide, at least, a zip code.');
		tf.elements['zip'].focus();
	}
}
