function setupLocator(options) {
  var defaults = {
    bulletPng: ('bulletPng' in options) ? options.bulletPng : '/dealer/bullet',
    shadowPng: ('shadowPng' in options) ? options.shadowPng : '/dealer/shadow',
    metric: ('metric' in options) ? options.metric : true,
    useCategories: ('useCategories' in options) ? options.useCategories : true,
    categoriesLabel: ('categoriesLabel' in options) ? options.categoriesLabel : 'Products',
    startLat: ('startLat' in options) ? options.startLat : 37.37,
    startLong: ('startLong' in options) ? options.startLong : -95.89,
    startZoom: ('startZoom' in options) ? options.startZoom : 4,
    geolocatorSuffix: ('geolocatorSuffix' in options) ? options.geolocatorSuffix : '',
    text: {
      'PRINT': 'PRINT',
      'CLOSE': 'CLOSE',
      'Phone': 'Phone',
      'Fax': 'Fax',
      'Contact': 'Contact',
      'Address not found': 'Address not found'
    }
  };
  
  if ('text' in options) {
    for (phrase in defaults.text) {
      if (phrase in options.text) {
        defaults.text[phrase] = options.text[phrase];
      }
    }
  }
  
  var map;
  var geocoder;
  var icons = [];
  
  // Prepare an HTML representation of a single location.
  function formatLocationInfo(location, printable) {
    var html = '';

    if (printable) {
      html += '<b>' + location['name'] + '</b>';
      if (defaults.metric) {
        var km = location['distance'] / 1000;
        html += ' (' + km.toFixed(1) + ' km)';
      }
      else {
        var miles = location['distance'] * 0.000621371192;
        html += ' (' + miles.toFixed(1) + ' mi)';
      }
    }
    else {
      if (location['url']) {
        html += '<h2><a href="' + location['url'] + '" target="_blank">' + location['name'] + '</a></h2>';
      }
      else {
        html += '<h2>' + location['name'] + '</h2>';
      }
    }
  
    if (location['address1']) {
      html += '<div>' + location['address1'] + '</div>';
    }
    if (location['address2']) {
      html += '<div>' + location['address2'] + '</div>';
    }
    if (location['city'] || location['state'] || location['zip']) {
      var address3 = '';
      if (location['city'] && location['state']) {
        address3 = location['city'] + ', ' + location['state'];
      }
      else {
        address3 = location['city'] + location['state'];
      }
      address3 = address3 ? (address3 + ' ') : address3;
      address3 += location['zip'];
      html += '<div>' + address3 + '</div>';
    }
    if (location['country']) {
      html += '<div>' + location['country'] + '</div>';
    }

    if (location['phone']) {
      html += '<div>' + defaults.text['Phone'] + ': ' + location['phone'] + '</div>';
    }
    if (location['fax']) {
      html += '<div>' + defaults.text['Fax'] + ': ' + location['fax'] + '</div>';
    }

    if (location['contact']) {
      if (location['email']) {
        html += '<div>' + defaults.text['Contact'] + ': <a href="mailto:' + location['email'] + '">' + location['contact'] + '</a></div>';
      }
      else {
        html += '<div>' + defaults.text['Contact'] + ': ' + location['contact'] + '</div>';
      }
    }
    else if (location['email']) {
      html += '<div>' + defaults.text['Contact'] + ': <a href="mailto:' + location['email'] + '">' + location['email'] + '</a></div>';
    }
        
    if (defaults.useCategories) {
      html += '<div class="categories" id="dist-loc-individual-categories">';
      html += '<b>' + defaults.categoriesLabel + ':</b> ';
      html += categories.join(', ');
      html += '</div>';
    }
    return html;
  }

  // Perform a search for a given address.
  function showAddress(address, category) {
    address = address + defaults.geolocatorSuffix;
    geocoder.getLatLng(address, function(point) {
      if (!point) {
        alert(defaults.text['Address not found'] + ":\n" + address);
      } else {
        map.clearOverlays();
        map.setCenter(point, 10);
        var marker = new GMarker(point);
        map.addOverlay(marker);
      
        var newResults = [];
        for (var i = 0; i < locations.length; i++) {
          var location = locations[i];
          if (!category || location['categories'][category]) {
            location['distance'] = point.distanceFrom(new GLatLng(locations[i]['lat'], locations[i]['lng']));
            newResults.push(location);
          }
        }
        newResults.sort(function(a, b) {
          if (a['distance'] < b['distance']) return -1;
          if (a['distance'] > b['distance']) return 1;
          return 0;
        });

        // Remove all the previous results
        var results = document.getElementById('dist-loc-results');
        while (results.firstChild) {
          results.removeChild(results.firstChild);
        }
        var printResults = document.getElementById('dist-loc-print-results');
        while (results.firstChild) {
          results.removeChild(results.firstChild);
        }
      
        // Show new results
        for (var i = 0; i < newResults.length && i < 10; i++) {
          showLocation(newResults[i], results, printResults, i);
        }
      
        var displayAddress = address.replace(/&/g, '&amp;');
        displayAddress = displayAddress.replace(/</g, '&lt;');
        displayAddress = displayAddress.replace(/>/g, '&gt;');
        document.getElementById('dist-loc-search-echo').innerHTML = displayAddress + ':';

        // Swap out instructions for results
        document.getElementById('dist-loc-instructions').style.display = 'none';
        document.getElementById('dist-loc-results-box').style.display = 'block';
      }
    });
  }

  // Show a single location on the map.
  function showLocation(location, results, printResults, index) {
    var point = new GLatLng(location['lat'], location['lng']);
    var marker = new GMarker(point, icons[index]);

    var showInfoWindow = function() {
      var categories = [];
      if (defaults.useCategories) {
        for (var category in location['categories']) {
          categories.push(category);
        }
      }

      var oldButton = document.getElementById('dist-loc-print-individual');
      if (oldButton) {
        oldButton.parentNode.removeChild(oldButton);
      }

      var html = formatLocationInfo(location);
      
      html += '<div><a href="index.htm" id="dist-loc-print-individual">' + defaults.text['PRINT'] + '</a></div>';
      marker.openInfoWindowHtml(html);
      document.getElementById('dist-loc-print-individual').onclick = function() {
        var printWindow = window.open('', 'print', 'width=200, height=200, toolbar=no, location=no, resizable=yes');
        if (printWindow) {
          printWindow.document.title = location['name'];
          var container = printWindow.document.createElement('div');
          container.style.borderColor = '#b0b0b0';
          container.style.borderWidth = '1px';
          container.style.borderStyle = 'solid';
          container.innerHTML = html;
          printWindow.document.body.appendChild(container);
          var bodies = printWindow.document.getElementsByTagName('body');
          for (i = 0; i < bodies.length; i++) {
            var bodyStyle = bodies[i].style;
            bodyStyle.backgroundColor = '#ffffff';
            bodyStyle.fontFamily = 'Arial, Helvetica, sans-serif';
            bodyStyle.fontSize = '75%';
            bodyStyle.marginLeft = bodyStyle.marginTop = bodyStyle.marginRight = bodyStyle.marginBottom = '10px';
            bodyStyle.paddingLeft = bodyStyle.paddingTop = bodyStyle.paddingRight = bodyStyle.paddingBottom = '10px';
          }
          var divs = container.getElementsByTagName('div');
          for (i = 0; i < divs.length; i++) {
            var divStyle = divs[i].style;
            divStyle.marginLeft = divStyle.marginRight = '6px';
            divStyle.marginBottom = '2px';
          }
          var as = container.getElementsByTagName('a');
          for (i = 0; i < as.length; i++) {
            var aStyle = as[i].style;
            aStyle.cursor = 'pointer';
            aStyle.textDecoration = 'underline';
            aStyle.color = '#5FA3D2';
          }
          var h2s = container.getElementsByTagName('h2');
          for (i = 0; i < h2s.length; i++) {
            var h2Style = h2s[i].style;
            h2Style.fontSize = '1.2em';
            h2Style.fontWeight = 'bold';
            h2Style.textAlign = 'center';
            h2Style.color = '#ffffff';
            h2Style.backgroundColor = '#666666';
            h2Style.marginLeft = h2Style.marginTop = h2Style.marginRight = h2Style.marginBottom = '3px';
            h2Style.paddingLeft = h2Style.paddingTop = h2Style.paddingRight = h2Style.paddingBottom = '3px';
            var as = h2s[i].getElementsByTagName('a');
            for (i = 0; i < as.length; i++) {
              var aStyle = as[i].style;
              aStyle.color = '#ffffff';
              aStyle.textDecoration = 'none';
            }
          }
      
          if (defaults.useCategories) {
            printWindow.document.getElementById('dist-loc-individual-categories').style.marginTop = '1em';
          }

          var button = printWindow.document.getElementById('dist-loc-print-individual');
          button.innerHTML = defaults.text['CLOSE'];
          button.onclick = function() {
            printWindow.close();
          };
          printWindow.print();
        }
        return false;
      };
    };

    GEvent.addListener(marker, 'click', showInfoWindow);
    map.addOverlay(marker);

    var result = document.createElement('li');
    var html = (index + 1) + '. ';
    html += '<b>' + location['name'] + '</b>';
    if (defaults.metric) {
      var km = location['distance'] / 1000;
      html += ' (' + km.toFixed(1) + ' km)';
    }
    else {
      var miles = location['distance'] * 0.000621371192;
      html += ' (' + miles.toFixed(1) + ' mi)';
    }
    result.innerHTML = html;
    result.onclick = showInfoWindow;
    result.className = index % 2 ? 'odd' : 'even';
    results.appendChild(result);
  
    result = document.createElement('li');
    html = formatLocationInfo(location, true);
    result.innerHTML = html;
    result.className = index % 2 ? 'odd' : 'even';
    printResults.appendChild(result);
  }
  
  window.onload = function() {
    if (GBrowserIsCompatible()) {
      map = new GMap2(document.getElementById('dist-loc-map'));
      map.addControl(new GLargeMapControl());
      map.addControl(new GMapTypeControl());
      map.addControl(new GScaleControl());
      map.addControl(new GOverviewMapControl());
      map.setCenter(new GLatLng(defaults.startLat, defaults.startLong), defaults.startZoom);

      for (var i = 0; i < 10; i++) {
        var icon = new GIcon();
        icon.image = defaults.bulletPng + (i + 1) + '.png';
        icon.shadow = defaults.shadowPng + '.png';
        icon.iconSize = new GSize(22, 27);
        icon.shadowSize = new GSize(41, 18);
        icon.iconAnchor = new GPoint(11, 26);
        icon.infoWindowAnchor = new GPoint(11, 3);
        icons[i] = icon;
      }

      geocoder = new GClientGeocoder();
  
      if (defaults.useCategories) {
        document.getElementById('dist-loc-search-form').onsubmit =
          document.getElementById('dist-loc-category').onchange = function() {
          showAddress(document.getElementById('dist-loc-search').value, document.getElementById('dist-loc-category').value);
          return false;
        };
      }
      else {
        document.getElementById('dist-loc-search-form').onsubmit = function() {
          showAddress(document.getElementById('dist-loc-search').value);
          return false;
        };
      }
    }
  
    document.getElementById('dist-loc-print-results-button').onclick = function() {
      document.getElementById('dist-loc').style.display = 'none';
      document.getElementById('dist-loc-print').style.display = 'block';
      window.print();
      return false;
    };
    document.getElementById('dist-loc-print-results-back').onclick = function() {
      document.getElementById('dist-loc').style.display = 'block';
      document.getElementById('dist-loc-print').style.display = 'none';
      return false;
    };
  };
  window.onunload = GUnload;
}
