/* Copyright (C) 2007 Michael Kosowsky  All rights reserved. */

/***************

nomenclature:

query refers to the lat/lon/elevation/... we're putting together to request from the server

answer refers to an earlier query; e.g. something we can elect to view
       we store the answers you've received in a cookie
       we build a list of answers from URL querystrings and your cookie and a the list of public answers
result is an answer we're displaying
an "answer" is just enough to help the user select (id, name, public status); a "result" is much richer
while "answers" could be called "results", I'm hoping different words will make this a bit easier

when we submit a query, it gets queued and we'll set the is_pending flag
occassionally we'll check all the is_pending entries
it turns out that we know enough about the answer -- name, lat, lon, ... -- for our UI,
so checking the entry simply means potentially flipping the is_pending flag,
and alerting the user the results are ready

when viewing 'all', map markers are answers; when viewing a result, markers are peaks.
refer to them generically as 'elements' in select_element()

in each peak or answer we expect
   id       to tie to a div in list_pane ("list_el_<id>")  (for answers this is the result id; for peaks it's just a serial number)
   lat
   lon
   az	    for peaks only, to draw the line on the silhouette
   marker   Gmarker associated with the element

to each marker we add:
  _element  the corresponding answer or peak
  _msg      content for InfoWindow


we DON'T bother with an array of markers because for answers, we want to be able to remove them at will

****************/



					//************************** GENERIC UI

var current = {
  show_all: true,
  print_layout: false,
  query_is_visible: false,
  sponsor_is_visible: false,
  show_transect: false,
  magnetic: true,

		  // cursor position on map
  lat: 0,
  lon: 0,
  latlng: null,
  elev: null,

  viewer_marker: null,
  latlon_marker: null,
  selected_element: null,
  bearing: null,

  use_metric: 0,
  degrees_format: 0
};

var result = {
  id: null,
  name: '',
  status: '',

  viewer_lat: 0,
  viewer_lon: 0,
  viewer_latlng: null,

  elev: 0,
  elev_above_ground: 0,
  is_public: false,
  declination: 0,
  queued_time: 0,
  start_time: 0,
  end_time: 0
};


var answers = [];
var peaks = [];
var limits = [];
var sil_parms = {};	// parameters (width, height, ...) for horizontal and vertical silhouettes

var centerline;
var list_highlight;	// currently highlighted div in the list pane (answer or peak)


function reset_display() {
  list_highlight = null;
  centerline = null;
  current.viewer_marker = null;
  current.latlon_marker = null;
  current.selected_element = null;
  map.clearOverlays();
  silmarkerstyle.display = 'none';
  disable_transect();
  //contour_widget.hide();
  contour_widget.reset();
  cloak_widget.hide();
  cloak_widget.reset();
  //maximize_widget.hide();
  clear_bearing();
}


function set_display_style(a, val) {
  a._foreach(function(s) { document.getElementById(s).style.display = val; });
}


function set_panes() {
  var off = ['sil_pane', 'sil_print_pane',
	     'all_top_pane', 'answer_top_pane',
	     'list_pane', 'list_print_pane', 'title_print_pane',
	     'query_pane', 'transect_pane',
	     'map_pane', 'bearing_div', 'recenter_div',
	     'topbutton_div', 'ads_pane', 'sponsor_pane' ];

  var on =  current.query_is_visible? ['query_pane', 'map_pane', 'topbutton_div', 'ads_pane']
          : current.show_all?         ['all_top_pane', 'list_pane', 'map_pane', 'topbutton_div', 'ads_pane']
	  : current.print_layout?     ['sil_print_pane', 'list_print_pane', 'title_print_pane']
          :                           ['sil_pane', 'list_pane', 'answer_top_pane', 'map_pane', 'recenter_div', 'topbutton_div', 'ads_pane'];

  set_display_style(off, 'none');
  set_display_style(on,  '');
}


function set_tabs() {
  ['all_tab', 'view_tab', 'print_tab', 'query_tab']._foreach(function(s) { document.getElementById(s).className = 'closedtab'; });

  document.getElementById(
	  current.query_is_visible? 'query_tab'
	: current.show_all?         'all_tab'
	: current.print_layout?     'print_tab'
	:                           'view_tab').className = 'opentab';

  document.getElementById('print_tab').style.display =
	current.show_all || current.query_is_visible? 'none' : '';
}



function set_units(use_metric) {
  current.use_metric = use_metric;
  radio_set(document.f_settings.units, current.use_metric);
  set_cookie('units', current.use_metric);
  document.getElementById('query_elev_units').innerHTML = current.use_metric? 'meters' : 'feet';
  document.getElementById('query_elev_default').innerHTML = current.use_metric? '2 meters' : '6 feet';
  document.f_query.radius_feet.style.display   = current.use_metric? 'none' : '';
  document.f_query.radius_meters.style.display = current.use_metric? '' : 'none';
  redraw_contour_overlay();
  redraw_for_format_change();
  if (current.show_transect && document.f_transect.show_axes.checked)
    draw_transect();
}

function set_degrees_format(degrees_format) {
  current.degrees_format = degrees_format % 3;
  radio_set(document.f_settings.degrees_format, current.degrees_format);
  set_cookie('df', current.degrees_format);
  redraw_for_format_change();
}

function redraw_for_format_change() {
  update_latlon_div();

  if (!current.show_all && !current.query_is_visible) {
    map.closeInfoWindow();
    add_viewer_marker();
    write_result_title();
    write_peak_list_div_and_markers();
    if (current.bearing)
	draw_bearing();
  }
}


var before_query_was_show_result;

function show_query() {
  //if (current.print_layout)
  //  show_result(result.id, false, current.magnetic);
  before_query_was_show_result = !current.show_all;
  reset_display();
  current.show_all = false;
  current.sponsor_is_visible = false;
  current.query_is_visible = true;
  set_panes();
  set_tabs();
  contour_zoom_callback(0, map.getZoom());
  set_lat_lon(current.lat, current.lon);

  wt_async_request('bin/query_estimate.cgi', 'QUERY ESTIMATE',
    function(minutes) {
      query_estimate_text.innerHTML = 'Requests are taking about ' + minutes + (minutes == 1?' minute' : ' minutes');
      document.getElementById('sponsor_estimate_text').innerHTML =
				      'Your panorama will be ready in about ' + minutes + (minutes == 1?' minute' : ' minutes');
    }
  );
}


function hide_query() {
  current.query_is_visible = false;
  if (before_query_was_show_result && result.id)
    show_result(result.id, current.print_layout, current.magnetic);
  else
    show_all();
}


function show_sponsor(url, ad, beta) {
  document.getElementById('sponsor_show_result').style.display = result.id? '' : 'none';
  document.getElementById('sponsor_beta_div').style.display    = beta?      '' : 'none';
  if (url) {
    document.getElementById('sponsor_url_div').style.display = '';
    document.getElementById('sponsor_url').href = url;
  } else {
    document.getElementById('sponsor_url_div').style.display = 'none';
  }
  document.getElementById('sponsor_ad_div').innerHTML = ad;

  var s = document.getElementById('sponsor_pane').style;
  s.height  = document.body.offsetHeight + 'px';
  s.width   = '100%';
  s.display = 'block';

  current.sponsor_is_visible = true;
}


function show_faq() {
  window.open('faq.html', '_faq', 'height=600,width=500,scrollbars');
}

function show_google_earth() {
  location.href = results_file(result.id, 'wt.kmz');
}

var seen_sky_warning = 0;
function show_google_sky() {
  if (result.id
      && (seen_sky_warning
          || confirm("Click 'OK' and you'll receive a Network Link for Google Earth that will you show your horizon in the sky whenever you refresh it.  Requires the Google Sky functionality in beta 4.2 or later of Google Earth."))) {
    seen_sky_warning = 1;
    location.href = 'bin/planisphere_link.kml?id=' + result.id + '&tz=' + default_timezone();
  }
}

function show_all_google_earth() {
  location.href = 'bin/update_kml.kml';
}

function email_us() {
  location.href = 'mailto:comments-main@hey' + '' + 'what' + 'sthat.com';
}

function email_result() {
  if (result.id)
    location.href = 'mailto:?subject=HeyWhatsThat panorama&body=Take a look at http://www.heywhatsthat.com/?view=' + result.id + '\n';
}

function select_element(e) {
  if (!e)
    return;

  current.selected_element = e;

  var m = e.marker;		// corresponding marker
  set_lat_lon(e.lat, e.lon, e.elev);
  m.openInfoWindowHtml(m._msg);

  if (list_highlight)
    list_highlight.style.backgroundColor = 'white';

  list_highlight = document.getElementById('list_el_' + e.id);
  if (list_highlight) {
    list_highlight.style.backgroundColor = '#dae9be';
    scroll_if_needed(list_highlight, list_div)
  }

  if (!current.show_all)
    draw_lines(e.az, e.range);
}

function select_home(recenter, open_info_window) {
  if (!open_info_window)
    map.closeInfoWindow;
  current.selected_element = null;
  set_lat_lon(result.viewer_lat, result.viewer_lon, null, recenter, 1, open_info_window);
  erase_lines();
}


function clear_list_highlight() {
  if (list_highlight) {
    list_highlight.style.backgroundColor = 'white';
    list_highlight = null;
  }
}

var set_lat_lon_timeout_id = 0;
var draw_centerline_too = 0;
var set_to_viewer_marker = 0;
var open_viewer_marker_infowindow = 0;

function set_lat_lon(lat, lon, elev, set_center, its_the_viewer_marker, _open_viewer_marker_infowindow) {
  current.lat    = lat;
  current.lon    = lon;
  current.latlng = new GLatLng(lat, lon);
  current.elev   = elev;
  document.f_query.lat.value = lat;
  document.f_query.lon.value = lon;
  set_to_viewer_marker          = its_the_viewer_marker;
  open_viewer_marker_infowindow = _open_viewer_marker_infowindow;


  // if (state.query_form_visible) push onto stack (I guess without elev)
  // map.closeInfoWindow();

  if (current.latlon_marker)
    map.removeOverlay(current.latlon_marker);
  current.latlon_marker = new GMarker(current.latlng,
			{ icon: icon_plus, clickable: false, title: format_latlon(lat, lon, current.degrees_format, 0) });
	  // If we addOverlay immediately, the map never sees a double-click
  if (set_lat_lon_timeout_id)
    clearTimeout(set_lat_lon_timeout_id);
  set_lat_lon_timeout_id = setTimeout(set_lat_lon_timeout, 300);

  if (set_center)
    map.setCenter(current.latlng);
  else if (!map.getBounds().contains(current.latlng))
    map.panTo(current.latlng);

  update_latlon_div();

  if (current.elev == null) {
    get_elev(current.lat, current.lon, function(lat, lon, elev) {
      if (current.lat == lat && current.lon == lon) {
	current.elev = elev;
	update_latlon_div();
      }
    });
  } else {
    set_elev(current.lat, current.lon, current.elev);
  }
}

function set_lat_lon_timeout() {
  map.addOverlay(current.latlon_marker);

	// Arghhh ... if we happen to place latlon_marker above viewer_marker, we can't click on the purple X!
  if (set_to_viewer_marker && current.viewer_marker) {
    add_viewer_marker();
    if (open_viewer_marker_infowindow)
      current.viewer_marker.openInfoWindowHtml(current.viewer_marker._msg);
  }

  if (draw_centerline_too) {
    if (centerline)
      map.addOverlay(centerline);
    draw_centerline_too = 0;
  }

  set_lat_lon_timeout_id = 0;
}


function update_latlon_div() {
  map_latlon_div.innerHTML =   format_latlon(current.lat, current.lon, current.degrees_format, 1)
			     + (current.elev != null? ' ' + feet_or_meters(current.elev, 1) : '');
}


				//************************** ANSWERS UI

function write_answer_list_div() {
  var s = '';
  for (var i = 0; i < answers.length; i++) {
    r = answers[i];
    s +=  '<div id="list_el_' + r.id + '">'
			// can't rely on index into answers array (e.g. 'select_element(answers[i])'),
			// because we might remove_answer()
	+ answer_list_innerhtml(r)
	+ '</div>';
  }
  set_list_div_style('300px', answers.length > 18);
  list_div.innerHTML = s;
}

function answer_list_innerhtml(r) {
  return '' +
      '<div onclick="select_element(find_answer_by_id(\'' + r.id + '\'))"><b>' + r.name.substr(0,40) + '</b></div>'
    + '&nbsp;&nbsp;'
    + (!r.is_pending?
	  '<a href="javascript:show_result(\'' + r.id + '\', false, true)">view</a> '
        : '')
    + (!r.is_pending && r.is_mine && r.is_public?
	  '<a class="listbutton" href="javascript:map.closeInfoWindow();set_public_by_id(\'' + r.id + '\', 0)">make private</a> '
	: '')
    + (!r.is_pending && r.is_mine && !r.is_public?
	  '<a class="listbutton" href="javascript:map.closeInfoWindow();set_public_by_id(\'' + r.id + '\', 1)">make public</a> '
	: '')
    + (!r.is_pending && r.is_mine?
	  '<a class="listbutton" href="javascript:map.closeInfoWindow();remove_answer_by_id(\'' + r.id + '\')">remove</a><br>'
	: '')
    + (r.is_pending?
	  '<i>pending</i><br>'
	: '');
}


function add_answer_markers() {
  for (var i = 0; i < answers.length; i++)
    add_answer_marker(answers[i]);
}


function add_answer_marker(r) {
  var l = new GLatLng(r.lat, r.lon);
  var marker = new GMarker(l, { icon: r.is_mine? r.is_public? icon_my_public_answer : icon_my_private_answer : icon_public_answer, title: r.name });
  r.marker = marker;
  marker._element = r;
  marker._msg = answer_marker_msg(r);
  GEvent.addListener(marker, "click", function() { select_element(this._element); } );
  map.addOverlay(marker);
}


function answer_marker_msg(r) {
  return '' +
      '<b>' + r.name + '</b><br>'
    + format_latlon(r.lat, r.lon, current.degrees_format, 1) + '<br>'
        // + r.lat + '&deg; ' + r.lon + '&deg;<br>'
    + (!r.is_pending?
	  '<a class="iwbutton" href="javascript:show_result(\'' + r.id + '\', false, true)">View</a><br>'
	: '')
    + (!r.is_pending && r.is_mine &&  r.is_public?
	  '<a class="iwbutton" href="javascript:map.closeInfoWindow();set_public_by_id(\'' + r.id + '\', 0)">Make private</a><br>'
	: '')
    + (!r.is_pending && r.is_mine && !r.is_public?
	  '<a class="iwbutton" href="javascript:map.closeInfoWindow();set_public_by_id(\'' + r.id + '\', 1)">Make public</a><br>'
	: '')
    + (!r.is_pending && r.is_mine?
	  '<a class="iwbutton" href="javascript:map.closeInfoWindow();remove_answer_by_id(\'' + r.id + '\')">Remove</a><br>'
	: '')
    + (r.is_pending?
	  '<i>pending</i>'
	: '')
}


				//************************** RESULT UI

function write_peak_list_div_and_markers() {
  var footer = 		     '<p><i>&nbsp;&nbsp;&nbsp;(Bearings are '
	           + (current.magnetic? 'magnetic; for true' : 'true; for magnetic')
		   + (current.print_layout? '<br>&nbsp;&nbsp;&nbsp;bearings ' : ' bearings<br>&nbsp;&nbsp;&nbsp;')
		   + ((result.declination >= 0) == current.magnetic? 'add ' : 'subtract ')
		   + Math.abs(result.declination)
		   + '&deg; or click <a href="javascript:show_result(result.id,current.print_layout,!current.magnetic)">here</a>)</i>'


  var s = '';

  for (var i = 0; i < peaks.length; i++) {
    var p = peaks[i];
    var l = new GLatLng(p.lat, p.lon);
    var marker = new GMarker(l, { icon: type_icons[p.type] || default_type_icon, title: p.name });
    p.id = i;
    p.marker = marker;

    var shown_at = '';
    if (p.true_lat != p.lat || p.true_lon != p.lon)
	shown_at = format_latlon(p.lat, p.lon, current.degrees_format, 1);
    if (p.true_elev != p.elev)
	shown_at += (shown_at? ' ' : '') + feet_or_meters(p.elev, 1);
    if (shown_at)
	shown_at = '(shown at ' + shown_at + ')';

    marker._element = p;
    marker._msg = '<b>' + p.name + '</b><br>'
		  + round0(p.az) + '&deg; '
		  + miles_or_km(p.range, 1) + '<br>'
		  + '<small>'
		    + format_latlon(p.true_lat, p.true_lon, current.degrees_format, 1) + '&nbsp;'
                    + feet_or_meters(p.true_elev, 1) + '<br>'
		    + shown_at
		  + '</small>';

    if (!current.print_layout)
      GEvent.addListener(marker, "click", function() { select_element(this._element); } );

    map.addOverlay(marker);

    if (current.print_layout)
      s += peaks.length >= 40? '<tr style="font-size:10px">' : '<tr>';
    else
      s += '<tr id=list_el_' + i + ' onclick=select_element(peaks[' + i + '])>';

    s +=   '<td align=right>' + round0(p.az)      + '&deg;</td>'
	 + '<td style="' + (type_styles[p.type] != null? type_styles[p.type] : default_type_style)
         +            '"><b>' + p.name            + '</b></td>'
	 + '<td align=right>' + miles_or_km(p.range, 1)    + '</td>'
	 + '<td align=right>' + feet_or_meters(p.true_elev, 1) + '</td>'
	 + '</tr>';
  }


  if (current.print_layout) {
    document.getElementById("list_print_div").innerHTML = '<table style="border-spacing: 15px 0px">' + s + '</table>' + footer;
  } else {
    set_list_div_style('375px', peaks.length > 38);
    list_div.innerHTML = '<table>' + s + '</table>' + footer;
  }
}

function add_viewer_marker() {
  if (current.viewer_marker)
    map.removeOverlay(current.viewer_marker);
  current.viewer_marker = new GMarker(result.viewer_latlng, { icon: icon_x, title: result.name });
  current.viewer_marker._msg =   '<b>' + (result.name || 'Home') + '</b><br>'
		               + format_latlon(result.viewer_lat, result.viewer_lon, current.degrees_format, 1) + '<br>'
			       + 'view is from ' + feet_or_meters(result.elev_above_ground, 1) + ' above ground<br>'
                               + '&nbsp;&nbsp;(' + feet_or_meters(result.elev, 1) + ' above sea level)';
  GEvent.addListener(current.viewer_marker, "click", function() { select_home(0, 1); });
  map.addOverlay(current.viewer_marker);
}

function draw_lines(az, range) {
  if (centerline)
    map.removeOverlay(centerline);
  centerline = new GPolyline([result.viewer_latlng, current.latlng], '#8B6914', 3, .5);
	// can't just map.addOverlay(centerline) here, because if the line extends under
	// the current mouse position, it will interfere with double click detection
	// Note that we only call draw_lines after a call to set_lat_lon, so we'll piggyback
	// on the timer we set there
  draw_centerline_too = 1;

  var x = az - sil_parms.first_segment * 360 / sil_parms.n_segments;
  if (x < 0)
    x += 360;
  silmarkerstyle.left = (sil_parms.az_scale * (x - sil_parms.az_min) + silmarker_offsetleft()) + 'px';
  silmarkerstyle.display = '';

  if (!range) {
    var a = bearing_and_range(result.viewer_lat, result.viewer_lon, current.lat, current.lon);
    range = a[1];
  }
  show_bearing(az, range);
  draw_transect();
}


function show_bearing(az, range) {
  var a = bearing_and_range(current.lat, current.lon, result.viewer_lat, result.viewer_lon);
  current.bearing = { az: az, reverse_az: adjust_for_declination(a[0]), range: range };
  draw_bearing();
}

function draw_bearing() {
  bearing_div.innerHTML = 'bearing ' + round0(current.bearing.az) + '&deg; ' + miles_or_km(current.bearing.range, 1);
  bearing_div.title = 'reverse bearing ' + round0(current.bearing.reverse_az);
  bearing_div.style.display = '';
}

function clear_bearing() {
  current.bearing = null;
  bearing_div.style.display = 'none';
}


function erase_lines() {
  if (centerline)
    map.removeOverlay(centerline);
  silmarkerstyle.display = 'none';
  clear_bearing();
  clear_transect();
}


function silhouetteclick(pane, event) {
  if (current.show_all)
    return;
  var az = ((is_msie? event.x : event.layerX) - silmarker_offsetleft()) / sil_parms.az_scale + sil_parms.az_min;
  az += sil_parms.first_segment * 360 / sil_parms.n_segments;
  if (az >= 360)
    az -= 360;

  var az_spread =  3 / sil_parms.az_scale;
  var alt_below = -4 / sil_parms.alt_scale;
  var alt_above =  7 / sil_parms.alt_scale;

	// if marker real close, use it
  for (var i = 0; i < peaks.length; i++) {
    if (peaks[i].az > az + az_spread)
      break;

		// click within several pixels of marker
		// (note that inverted triangle's hotspot is bottom of marker)
    if (Math.abs(az - peaks[i].az) <= az_spread) {
      var a = (sil_parms.height - (is_msie? event.y : event.layerY)) / sil_parms.alt_scale + sil_parms.alt_min;
      a -= peaks[i].alt;
      if (alt_below <= a && a <= alt_above) { 
	select_element(peaks[i]);
	return;
      }
    }
  }

	// no nearby peak.  just draw lines
  map.closeInfoWindow();
  current.selected_element = null;
  var a = limits[Math.floor(az)];
  set_lat_lon(a[0], a[1]);
  draw_lines(az);
  clear_list_highlight();
}

function show_silhouette() {
  if (current.show_all)
    return;

  silmarkerstyle.display = 'none';
  if (!sil_parms.ok)
    return;

	// NOTE: assumes segmented for 'view', non-segmented for 'print'
  if (current.print_layout)
    document.getElementById("sil_print_img").src = sil_parms.image_file_f();
  else
    for (var i = 0; i < sil_parms.n_segments; i++)
      document.getElementById("sil_img" + i).src = sil_parms.image_file_f((i + sil_parms.first_segment) % sil_parms.n_segments);
}

function segment_rotate(i) {
  if (i < 0)
    i = sil_parms.n_segments + (i % sil_parms.n_segments);
  sil_parms.first_segment = (sil_parms.first_segment + i) % sil_parms.n_segments;
  show_silhouette();
}

function silmarker_offsetleft() {
  return sil_img0.offsetLeft;
}


function read_image_parms(id, filename, sil) {
  var a = wt_request_array(results_file(id, filename), 'IMAGE DATA', function(a) { return a.length == 10; });
  if (!a) {
    sil.ok = 0;
    return;
  }
  sil.ok = 1;
			// IMAGE DATA
  sil.width      = a.shift() - 0;
  sil.az_min     = a.shift() - 0;
  sil.az_max     = a.shift() - 0;
  sil.az_scale   = a.shift() - 0;
  sil.height     = a.shift() - 0;
  sil.alt_min    = a.shift() - 0;
  sil.alt_max    = a.shift() - 0;
  sil.alt_scale  = a.shift() - 0;
  sil.headroom   = a.shift() - 0;
  sil.n_segments = a.shift() - 0 || 1;
  sil.first_segment = 0;
  if (sil.n_segments == 1) {
    sil.image_file_f = function(n) { return results_file(id, filename + '.png') };
  } else {
    sil.image_file_f = function(n) { return results_file(id, filename + n + '.png') };
  }
}



					//*****************************  CREATE THE VIEWS


function show_all() {
  reset_display();
  current.show_all = true;
  current.query_is_visible = false;
  current.sponsor_is_visible = false;
  set_panes();
  set_tabs();
  add_answer_markers();
  set_lat_lon(current.lat, current.lon);
  write_answer_list_div();
  set_view_menus_to('*');
	// this is initialized by the set_lat_lon() call in index.html
  //map.setCenter(new GLatLng(current.lat, current.lon), 7);  // DEFAULT SHOW ALL ZOOM LEVEL
  contour_zoom_callback(0, map.getZoom());
}


function show_result(id, _print_layout, _magnetic) {
  var a = wt_request_array(results_file(id, 'data'), 'DATA', function(a) { return a.length >= DATA_NAME_INDEX; });
  if (!a || a[0] != 'ok') {
		// if failure is on our default id, no message, and go to a display we know will work
		// otherwise display error and stay on current display
    if (id == startup_id) {
      show_all();
    } else {
	// DATA
      alert(  !a ? 'unknown panorama ' + id
                 : a.slice(DATA_NAME_INDEX).join(' ')
							// STATUS
	           + (a[0] == 'error'? ' failed (missing data)' : ' not ready')
           );
    }
    return;
  }

  reset_display();
  current.show_all = false;
  current.query_is_visible = false;
  current.sponsor_is_visible = false;
  current.print_layout = _print_layout;
  current.magnetic = _magnetic;
  set_panes();
  set_tabs();
  cloak_widget.show();
  //contour_widget.disable();
  //contour_widget.show();
  //maximize_widget.show();

			// DATA
  result = {
    id:			id,
    status:		a.shift(),
    viewer_lat:		a.shift() - 0,
    viewer_lon:		a.shift() - 0,
    elev:		a.shift() - 0,
    elev_above_ground:	a.shift() - 0,
    queued_time:	a.shift() - 0,
    start_time:		a.shift() - 0,
    end_time:		a.shift() - 0,
    is_public:		a.shift() - 0,
    declination:	a.shift() - 0,
    client:		a.shift(),
    name:		a.join(' ')
  };

  result.viewer_latlng = new GLatLng(result.viewer_lat, result.viewer_lon);

  if (current.print_layout) {
    read_image_parms(id, 'image_v' + (current.magnetic? 'm' : ''), sil_parms);
    document.getElementById('scale_print_status_div').innerHTML =
				'(vertical scale<br>exaggerated ' + round1(sil_parms.alt_scale / sil_parms.az_scale) + 'x)';

  } else {
    read_image_parms(id, 'image' + (current.magnetic? '_m' : ''), sil_parms);
    document.getElementById('scale_status_div').innerHTML =
				'(vertical scale exaggerated ' + round1(sil_parms.alt_scale / sil_parms.az_scale) + 'x)';
    silmarkerstyle.height = sil_parms.headroom + sil_parms.alt_scale * (sil_parms.alt_max - sil_parms.alt_min) + 'px';
  }

  show_silhouette();
  write_result_title();

	// az alt lat lon  elev range hrange name
  a = wt_request_array_of_lines(results_file(id, 'peaks'), 'PEAKS');
  peaks = [];
  if (a) {
    for (var i = 0; i < a.length; i++) {
      var aa = a[i].split(' ');
      if (aa.length < 7)
        continue;
      aa[0] -= 0;
      if (current.magnetic)
        aa[0] -= result.declination;
      if (aa[0] < 0)
        aa[0] += 360;

if (aa[7].length != 1)
		// new PEAKS format
      peaks.push({   az: aa[0],
	            alt: aa[1] - 0,
                    lat: aa[2] - 0,
                    lon: aa[3] - 0,
                   elev: aa[4] - 0,
                  range: aa[5] - 0,
                 hrange: aa[6] - 0,
               true_lat: aa[7] - 0,
               true_lon: aa[8] - 0,
              true_elev: aa[9] - 0,
	     visibility: aa[10],
                   type: aa[11],
		   name: aa.slice(12).join(' ') });
else
		// old PEAKS format (had V P N U in position 7)
      peaks.push({   az: aa[0],
	            alt: aa[1] - 0,
                    lat: aa[2] - 0,
                    lon: aa[3] - 0,
                   elev: aa[4] - 0,
                  range: aa[5] - 0,
                 hrange: aa[6] - 0,
               true_lat: aa[2] - 0,
               true_lon: aa[3] - 0,
              true_elev: aa[4] - 0,
	     visibility: aa[7],
                   type: aa[8],
		   name: aa.slice(9).join(' ') });
    }
  }
  peaks.sort(function (a, b) { return a.az - b.az;});

	// lat lon alt.  runs from 0 to 359.
  a = wt_request_array_of_lines(results_file(id, 'limits'), 'LIMITS');
  limits = [];
  for (var i = 0; i < a.length; i++) {
    var aa = a[i].split(' ');
    if (aa.length < 2)
      continue;
    limits.push([aa[0] - 0, aa[1] - 0]);
  }

	// we only use limits[] to map an azimuth to a lat/lon on the google map.
	// if we're currently using magnetic bearings, then the azimuth they click
	// on the panorama will be magnetic, so we need to adjust the limits array
	// appropriately
  if (current.magnetic && result.declination != 0)
    if (result.declination > 0)
      limits = limits.slice(result.declination).concat(limits.slice(0, result.declination - 1));
    else
      limits = limits.slice(360 + result.declination).concat(limits.slice(0, 360 + result.declination - 1));

  add_viewer_marker();
  map.setCenter(result.viewer_latlng, 11);  // DEFAULT ZOOM LEVEL

  write_peak_list_div_and_markers();
  recenter_text.innerHTML = result.name;

  north_widget.set_state(current.magnetic);
  transect_widget.set_state(current.show_transect);
  //cloak_widget.trigger(1);

  set_view_menus_to(id);
  set_lat_lon(result.viewer_lat, result.viewer_lon);
}


function write_result_title() {
  var s =   '<div class="bigtitle">'
          + result.name
	  + '</div>'
	  + '<div class="littletitle">'
          + 'latitude '  + format_lat(result.viewer_lat, current.degrees_format, 1) + ' '
          + 'longitude ' + format_lon(result.viewer_lon, current.degrees_format, 1) + '<br>'
	  + 'elevation ' + feet_or_meters(result.elev, 1) + ' above sea level (' + feet_or_meters(result.elev_above_ground, 1) + ' above ground)'
	  + (current.print_layout? '<br>http://www.heywhatsthat.com/?view=' + result.id : '')
	  + '</div>';

  if (current.print_layout)
    document.getElementById('title_print_pane').innerHTML = s;
  else
    answer_title_div.innerHTML = s;
}

					//******************************** QUERY HANDLING

				// BUG: only jump to new lat/lon once both have changed,
				//      or it's only a small (e.g. <1 degree) in just one of them,
				//      or a certain amount of time has passed

function handle_direct_lat_lon_entry() {
  if (!document.f_query.lat.value || !document.f_query.lon.value)
    return;
  var lat = read_angle(document.f_query.lat.value);
  var lon = read_angle(document.f_query.lon.value);
  set_lat_lon(lat, lon);
}


function handle_map_click(overlay, point) {
  if (point) {
    set_lat_lon(round6(point.y), round6(point.x));
    current.selected_element = null;
    clear_list_highlight();
    if (current.show_all || current.query_is_visible || current.print_layout)   //current.show_result
      return;

    var a = bearing_and_range(result.viewer_lat, result.viewer_lon, current.lat, current.lon);
    draw_lines(adjust_for_declination(a[0]), a[1]);
  }
}


function handle_location() {
  if (!document.f_query.location.value)
    return;

  geocode(document.f_query.location.value, function(l) {
      set_lat_lon(l.lat(), l.lng(), null, 1);
    }
  );
}


function handle_move() {
  var radius = current.use_metric? document.f_query.radius_meters.value : document.f_query.radius_feet.value;
	// returns lat lon
  wt_async_request_array('bin/highpoint.cgi?lat=' + current.lat + '&lon=' + current.lon + '&radius=' + radius,
	      'MOVE', function(a) { return a[0] != 0 && a[1] != 0; }, function(a) { set_lat_lon(a[0], a[1], a[2]); });
}


var seen_default_ad = 0;

function handle_query() {
  var name = document.f_query.panoramatitle.value;
  if (!name) {
    alert('Please enter a title');
    return;
  }

  handle_direct_lat_lon_entry();	// in case we never got an onchange ...
  if (!current.lat || !current.lon) {
    alert('Please enter latitude and longitude');
    return;
  }

  //if (current.lat > 60 || current.lat < -54) {
  if (current.lat < -54 || current.lat >= 71 || (current.lat > 60 && (current.lon < -173 || current.lon >= -139))) {
    alert('Invalid latitude.\nWe currently cover latitude 60N to 54S and most of Alaska.');
    return;
  }

  var elev;
  var elev_is_absolute;
  if (document.f_query.elev.value == '') {
    elev = current.use_metric? 2 : 6;
    elev_is_absolute = 0;
  } else {
    elev = document.f_query.elev.value - 0;
    elev_is_absolute = radio_get(document.f_query.absolute) - 0;
  }

/*****************
	// returns result's id then DATA array (saves a round trip)
  var a = wt_request_array('bin/query.cgi?'
	+ 'lat='               + current.lat
	+ '&lon='              + current.lon
	+ '&elev='             + units_to_meters(elev)
	+ '&elev_is_absolute=' + elev_is_absolute
	+ '&name='             + encodeURI_more(name)
	+ '&public=0'
	+ '&return_data=1'
	+ (query_client? '&client=' + query_client : ''),
    'QUERY',
    function(a) { return a.length >= DATA_NAME_INDEX + 1; })

  if (!a)
    return;
************************/
    // HACK first line is data array, subsequent lines are sponsor div
  var response = wt_request('bin/query.cgi?'
	+ 'lat='               + current.lat
	+ '&lon='              + current.lon
	+ '&elev='             + units_to_meters(elev)
	+ '&elev_is_absolute=' + elev_is_absolute
	+ '&name='             + encodeURI_more(name)
	+ '&public=0'
	+ '&return_data=1'
	+ (query_client? '&client=' + query_client : ''),
    'QUERY');

	// data line, optionally followed by sponsor url line and sponsor ad multiple lines
  if (!response)
    return;
  var response_parts = response.match(/(.*?)\n(?:(.*?)\n(.*))?/);
  if (!response_parts || !response_parts[1])
    return;
  var data = response_parts[1].split(' ');
  if (data.length < DATA_NAME_INDEX + 1)
    return;
  add_answer(data[0], 1, data.slice(1));
  set_view_menus();
  set_answer_cookie();

  document.f_query.panoramatitle.value = '';
  document.f_query.elev.value = '';
  radio_set(document.f_query.absolute, 0);

	// HACK.  client 'beta' if we're not sure of quality of coverage
        // if there's an ad, go to sponsor pane
        // if not, alert and STAY ON QUERY pane
  if (response_parts[3]) {
    show_sponsor(response_parts[2], response_parts[3], data[DATA_NAME_INDEX] == 'beta');

  } else if (!seen_default_ad) {
    seen_default_ad = 1;
    show_sponsor('/bin/sponsor.cgi?sponsor=0&url=http://www.fiddlersgreenfarm.com',
                 '<a href="/bin/sponsor.cgi?sponsor=0&url=http://www.fiddlersgreenfarm.com" target="_blank"><img src="http://www.fiddlersgreenfarm.com/img/logo300w.gif" width="300" height="211" border="0" alt="Fiddler\'s Green Farm - Whole grain goodness from Maine"></a>',
		data[DATA_NAME_INDEX] == 'beta');
  } else {
    alert('"' + name + '" submitted.\nYou\'ll be alerted when it\'s ready.'
          + (data[DATA_NAME_INDEX] == 'beta'? '\nNote that the non-US data is still being evaluated.' : ''));
  }
}



					//***************************** ANSWER LIST HANDLING
function add_answer(id, is_mine, a) {
    // if id already exists, possibly update is_mine, and if it's not pending we're done
  var r = find_answer_by_id(id);
  if (r) {
    if (is_mine && !r.is_mine) {
      r.is_mine = 1;
		// this isn't necessary, because is_mine settings only can change
		// at startup (e.g. not while show_all() is active)
      //update_answer_ui(r);
    }
    if (!r.is_pending)
      return;
  }

  if (!a) {
    a = wt_request_array(results_file(id, 'data'),
			    '', // no error message
			    function(a) { return a.length >= DATA_NAME_INDEX; });
    if (!a)
      return;
  }

  if (!r) {
    r = new Object;
    answers.unshift(r);  // later additions appear first in list
  }

  var was_pending = r.is_pending;
		// STATUS
  var is_pending  = (a[0] == 'queued' || a[0] == 'running' || a[0] == 'runningremote');

		// DATA
  r.id         = id;
  r.is_pending = is_pending;
  r.is_mine    = is_mine;
  r.status     = a[0];
  r.lat        = a[1] - 0;
  r.lon        = a[2] - 0;
  r.is_public  = a[DATA_PUBLIC_INDEX] - 0;
  r.name       = a.slice(DATA_NAME_INDEX).join(' ');

  if (was_pending && !is_pending)
		// this one is necessary, because it can happen anytime
    update_answer_ui(r);

  if (is_pending)
    start_pending_timer();
}


function update_answer_ui(r) {
  if (!current.show_all)
    return;
  if (current.selected_element == r)
    map.closeInfoWindow();
  if (r.marker) {
    map.removeOverlay(r.marker);
    add_answer_marker(r);
  }
  var e = document.getElementById('list_el_' + r.id);
  if (e)
    e.innerHTML = answer_list_innerhtml(r);
}

function find_answer_by_id(id) {
  for (var i = 0; i < answers.length; i++)
    if (answers[i].id == id)
      return answers[i];
  return null;
}

function find_answer_index_by_id(id) {
  for (var i = 0; i < answers.length; i++)
    if (answers[i].id == id)
      return i;
  return null;
}


function remove_answer_by_id(id) {
  var i = find_answer_index_by_id(id);
  if (i == null)
    return;
  var r = answers[i];
  if (!wt_request_array('bin/remove.cgi?id=' + r.id, 'REMOVE'))
    return;

  if (r.marker)
    map.removeOverlay(r.marker);
  if (current.selected_element == r && current.show_all)
    map.closeInfoWindow();
  answers.splice(i, 1);
  write_answer_list_div();
  set_view_menus();
  set_answer_cookie();
}


function set_public_by_id(id, is_public) {
  var r = find_answer_by_id(id);
  if (r)
    set_public(r, is_public);
}

	// BUG: add a layer of redirection (or an account structure) to public answers, so folks can't
	// can't make 'em private or remove 'em once they get the ID
function set_public(r, is_public) {
  var a = wt_request_array('bin/set_public.cgi?id=' + r.id + '&is_public=' + is_public, 'SET PUBLIC', function(a) { return a.length == 1; });
  if (!a)
    return;
  r.is_public = a[0] - 0;
  update_answer_ui(r);
}


function set_answer_cookie() {
  var a = [];
  for (var i = 0; i < answers.length; i++) {
    if (answers[i].is_mine)
      a.push(answers[i].id);
  }
  set_cookie('results', a.join(' ')); 
}


function read_answer_cookie() {
  var s = read_cookie('results');
  if (!s)
    return 0;
  var n = answers.length;
  s.split(' ')._foreach(function(id) { add_answer(id, 1); });
  set_answer_cookie();
  return answers.length - n;
}

function add_public_answers() {
  a = wt_request_array_of_lines('results/public', 'PUBLIC');
  for (var i = 0; i < a.length; i++) {
    var aa = a[i].split(' ');
    if (aa.length < 1 + DATA_NAME_INDEX+1)  // 1 for id, and REQUIRE a name
      continue;
    add_answer(aa[0], 0, aa.slice(1));
  }
}


function add_querystring_queries() {
	// view=id[+id ...]  just view
	// add=id[+id ...]   make it yours (e.g. add it to cookie)
	// print=id[+id ...] view in print layout
    words_param('add')._foreach(function(id) { add_answer(id, 1) });
   words_param('view')._foreach(function(id) { add_answer(id, 0) });
  words_param('print')._foreach(function(id) { add_answer(id, 0) });
}


function create_answers_list() {
  answers = [];
	// if there's a 'view=', show it at startup
  add_querystring_queries();
  var answer_to_show = answers.length? answers[0].id : null;
  var n_cookie_items = read_answer_cookie();
  add_public_answers();
  answers.sort(function(a, b) {
	if (a == answer_to_show) return -1;
	if (b == answer_to_show) return 1;
	if (a.is_mine != b.is_mine) return b.is_mine - a.is_mine;
	return a.name.localeCompare(b.name);
	//if (a.name < b.name) return -1;
	//if (a.name > b.name) return 1;
	//return 0;
  });
  set_answer_cookie();
  set_view_menus();
  return [n_cookie_items, answer_to_show];
}


var pending_timer = 0;

function start_pending_timer() {
  if (!pending_timer)
    pending_timer = setTimeout(check_pending_answers, 10000);
}


function check_pending_answers() {
  pending_timer = 0;
  answers._foreach(function(r) {
    if (r.is_pending)
      wt_async_request_array(results_file(r.id, 'data'),
			    'PENDING DATA',
			    function(a) { return a.length >= DATA_NAME_INDEX; },
			    function(a) { add_answer(r.id, r.is_mine, a);
					  if (r.is_pending)
					    return;
						// HACK: if status==error, we know we'll just be doing an alert, so we'll then need to break down window
					  if (current.sponsor_is_visible && r.status == 'error') {
                                            show_result(r.id, false, true);
					    show_query();
					    return;
					  }
					  if (current.sponsor_is_visible || r.status == 'error' || confirm("'" + r.name + "' is ready\nView it now?"))
                                            show_result(r.id, false, true);
					})
    });
}


					//***************************** SELECTION DROPDOWNS FOR CHOOSING RESULTS

function handle_select_view(sel) {
  if (sel.value == '*')
    show_all();
  else if (sel.value)
    show_result(sel.value, false, true);
}

function set_view_menu(sel, include_all) {
  sel.length = 0;
  //if (include_all)
  //  select_add_to_end(sel, new Option('All Panoramas', '*', 1, 1));

		// BUG: rather than arbitrarily limiting the number of panoramas,
		//   do some clever cascade (e.g. by state or by distance from current)
  for (var i = 0; i < answers.length && i < 2000; i++)
    select_add_to_end(sel, new Option(answers[i].name.substr(0,30), answers[i].id, 0, 0));
}

function set_view_menu_to(sel, id) {
  var o = sel.options;
  for (var i = 0; i < o.length; i++)
    if (o[i].value == id) {
      o[i].selected = 1;
      return;
    }
}

function set_view_menus() {
  set_view_menu(document.f_act.sel, 0);
}

function set_view_menus_to(id) {
  set_view_menu_to(document.f_act.sel, id);
}


					//****************************** VISIBILITY CLOAK
var cloak_overlays = [];

function show_cloak() {
  cloak_overlays = [];
  var a = wt_request_array_of_lines('bin/list_cloakm.cgi?id=' + result.id, 'CLOAK');
  if (!a)
    return;
  for (var i = 0; i < a.length; i++) {
    var b = CloakOverlayByName(a[i]);
    if (b) {
      cloak_overlays.push(b);
      map.addOverlay(b);
    }
  }
}

function remove_cloak() {
  for (var i = 0; i < cloak_overlays.length; i++)
    map.removeOverlay(cloak_overlays[i]);
  cloak_overlays = [];
}


/***********
function srtm_name(lat, lon) {
  var slat = '00'  + Math.abs(lat);
  var slon = '000' + Math.abs(lon);
  return (lat >= 0? 'N' : 'S') + slat.substr(slat.length - 2) + (lon >= 0? 'E' : 'W') + slon.substr(slon.length - 3);
}

function CloakOverlay(lat, lon, result) {
  return new OneDegreeImgOverlay(lat, lon, 1 + 3./3600, '/results/' + result + '/cloak' + srtm_name(lat, lon) + '.png');
}
************/

var srtm_re = /([NS])(\d\d)([EW])(\d\d\d)\./;

function srtm_latlon(s) {
  var a = s.match(srtm_re);
  if (!a || a.length != 5)
    return null;
  return [ (a[1] == 'N'? 1 : -1) * a[2], (a[3] == 'E'? 1 : -1) * a[4] ];
}

function CloakOverlayByName(name) {
  var a = srtm_latlon(name);
  if (!a)
    return null;
  return new OneDegreeImgOverlay(a[0], a[1], .5/3600, name);
}


					//*********************** IMGOVERLAY CLASS
function ImgOverlay(bounds, url) {
  this.bounds  = bounds;
  this.url     = url;
}

derive(ImgOverlay, GOverlay);

ImgOverlay.prototype.initialize = function(map) {
  this.map            = map;
  this.img            = document.createElement("img");
  this.img.src        = this.url;
  this.style          = this.img.style;
  this.style.position = "absolute";

  // Our image is flat against the map, so we add our selves to the MAP_PANE pane,
  // which is at the same z-index as the map itself (i.e., below the marker shadows)
  map.getPane(G_MAP_MAP_PANE).appendChild(this.img);
}

ImgOverlay.prototype.remove = function() {
  this.img.parentNode.removeChild(this.img);
}

ImgOverlay.prototype.copy = function() {
  return new ImgOverlay(this.bounds, this.url);
}

ImgOverlay.prototype.redraw = function(change_in_coordinate_system) {
  if (!change_in_coordinate_system)
    return;

  var sw   = this.map.fromLatLngToDivPixel(this.bounds.getSouthWest());
  var ne   = this.map.fromLatLngToDivPixel(this.bounds.getNorthEast());
  var s    = this.style;
  s.width  = (ne.x - sw.x) + "px";
  s.height = (sw.y - ne.y) + "px";
  s.left   = sw.x + "px";
  s.top    = ne.y + "px";
}

function OneDegreeImgOverlay(lat, lon, fudge, url) {
  return new ImgOverlay(new GLatLngBounds(new GLatLng(lat - fudge, lon - fudge), new GLatLng(lat + 1 + fudge, lon + 1 + fudge)), url);
}


					//***************************** CONTOURS
var contour_overlay;
var contour_widget;
var contour_listener;

/************
		         // 0     1     2     3     4     5     6     7    8    9   10   11   12   13  14  15  16  17
var contour_interval_ft_array = [2500, 2500, 2500, 2500, 2500, 1000, 1000, 1000, 500, 250, 100, 100, 100, 100, 50, 25, 10];

function contour_interval_ft(z) {
  if (z >= contour_interval_ft_array.length)
    return contour_interval_ft_array[contour_interval_ft_array.length - 1];
  return contour_interval_ft_array[z];
}

function contour_interval_with_units(z) {
  return contour_interval_ft(z) + ' ft';
}

function contour_interval(z) {
  return contour_interval_ft(z) / feet_per_meter;
}
******************/
				 // 0     1     2     3     4     5     6     7    8    9   10   11   12   13  14  15  16  17
var contour_interval_ft_array = [2500, 2500, 2500, 2500, 2500, 1000, 1000, 1000, 500, 250, 100, 100, 100, 100, 50, 25, 10];
var contour_interval_m_array  = [1000, 1000,  750,  750,  750,  250,  250,  250, 200, 100,  50,  50,  25,  25, 25, 10,  3];

function contour_interval_ft(z) {
  if (z >= contour_interval_ft_array.length)
    return contour_interval_ft_array[contour_interval_ft_array.length - 1];
  return contour_interval_ft_array[z];
}

function contour_interval_m(z) {
  if (z >= contour_interval_m_array.length)
    return contour_interval_m_array[contour_interval_m_array.length - 1];
  return contour_interval_m_array[z];
}

function contour_interval_with_units(z) {
  return current.use_metric? contour_interval_m(z) + ' m' : contour_interval_ft(z) + ' ft';
}

function contour_interval(z) {
  return current.use_metric? contour_interval_m(z) : contour_interval_ft(z) * meters_per_foot;
}

function redraw_contour_overlay() {
  if (contour_overlay) {
    show_contour_overlay();
    if (contour_widget)
      contour_widget.set_title();
  }
}

function show_contour_overlay() {
  if (contour_overlay)
    map.removeOverlay(contour_overlay);
  contour_overlay = new GTileLayerOverlay(TileLayer(0, 17, 'Contours (C) 2007', 'Michael Kosowsky',
	    function(point, zoom) {
	      // if (!contour_intervals[zoom])
	      //  return 'images/no-contour.png';
	      // force blue color if looking at anything other than 'map' type
		 return 'http://contour.heywhatsthat.com/bin/contour_tiles.cgi?x=' + point.x + '&y=' + point.y + '&zoom=' + zoom
		                           + '&interval=' + contour_interval(zoom)
					   + (map.getCurrentMapType().getName() != 'Map'? '&color=0000FF30' : '');
            }));
  map.addOverlay(contour_overlay);
}

function create_contour_widget(x, width) {
  WTGControl(map, x, width, 'Contours', '<b>Contours</b>',
    function() {
      return this.state?
		 'contour interval ' + contour_interval_with_units(map.getZoom())
               : 'Click to see contours';
    },
    function(i) {
      if (i) {
	show_contour_overlay();
      } else {
	if (contour_overlay)
          map.removeOverlay(contour_overlay);
	contour_overlay = null;
      }
    },
    function() {
      contour_widget = this;
    });

  contour_listener = GEvent.addListener(map, "zoomend", contour_zoom_callback);
}


function contour_zoom_callback(oldz, newz) {
  if (contour_widget)
    contour_widget.set_title();
}


	// Google Maps Bug: copyright stuff seems to only work for new map types, NOT for tile overlays ??
function TileLayer(min_zoom, max_zoom, copyright_prefix, copyright, url_function) {
  var cc = new GCopyrightCollection(copyright_prefix);
  cc.addCopyright(new GCopyright(2, new GLatLngBounds(new GLatLng(-54,-180), new GLatLng(60,180)), min_zoom, copyright));
  var t = new GTileLayer(cc, min_zoom, max_zoom);
  t.getTileUrl = url_function;
  return t;
}

					//***************************** TRANSECTS
var transect_widget;


function enable_and_draw_transect() {
  enable_transect();
  if (result.viewer_latlng.equals(current.latlng))
    document.getElementById('transect_img').src = 'images/profile-click.png';
  else
    draw_transect();
}

function enable_transect() {
  document.getElementById('transect_pane').style.display = '';
  current.show_transect = true;
}

function disable_transect(workaround_reflow_bug) {
  current.show_transect = false;
  document.getElementById('transect_img').src = '';
  document.getElementById('transect_pane').style.display = 'none';

  if (workaround_reflow_bug && !is_msie) {
		// REFLOW WORKAROUND
    document.getElementById('map_pane').style.display = 'none';
    setTimeout(function() { document.getElementById('map_pane').style.display = ''; }, 100);
  }
}

function draw_transect() {
  if (!current.show_transect || result.viewer_latlng.equals(current.latlng))
    return;

  document.getElementById('transect_img').src =
	    'bin/profile.cgi?src=main'
	  + '&pt0=' + result.viewer_lat + ',' + result.viewer_lon + ',8b6914,' + result.elev + ',9906ff'
          + '&pt1=' + current.lat       + ',' + current.lon
             + (current.selected_element?
		  ',,' + current.selected_element.elev + ',' + (type_colors[current.selected_element.type] || default_type_color)
                : '')
	  + '&curvature=' + (document.f_transect.flat_earth.checked? 0 : 1)
	  + '&axes='      + (document.f_transect.show_axes.checked? 1 : 0)
	  + '&metric='    + current.use_metric;
}


function clear_transect() {
  if (current.show_transect)
    document.getElementById('transect_img').src = 'images/no-profile.png';
}


					//**************************** ELEVATION DATA CACHE
var elevs = new Object;

function set_elev(lat, lon, elev) {
  elevs[round6(lat) + ' ' + round6(lon)] = elev;
}

function get_elev(lat, lon, callback) {
  var lat0 = round6(lat);
  var lon0 = round6(lon);
  if (elevs[lat0 + ' ' + lon0] != null)
    callback(lat, lon, elevs[lat0 + ' ' + lon0])
  else
		// request_array_of_lines may be appropriate, because points will take many lat/lon pairs at once
    wt_async_request_array('bin/points.cgi?lat0=' + lat0 + '&lon0=' + lon0, 'Elevation',
	function(a) {
	  return 1; //a.length == 3;
        },
	function(a) {
			// points.cgi returns lat/lon with 6 digits of precision, including trailing zeroes,
			// but round6() strips trailing zeroes
	  if (a.length == 3) {
	    elevs[round6(a[0]) + ' ' + round6(a[1])] = a[2];
	    callback(a[0], a[1], a[2]);
          }
        });
}



					//***************************** CONTROLS

function WTControl(parent, n_states, enablef, disablef, innerHTMLf, titlef, clickf) {
  this.div        = document.createElement('div');
  parent.appendChild(this.div);
  this.style      = this.div.style;
  this.n_states   = n_states;
  this.enablef    = enablef;
  this.disablef   = disablef;
  this.innerHTMLf = innerHTMLf;
  this.clickf     = clickf;
  this.titlef     = titlef;
  this.state      = 0;
  this.enabled    = 0;
  this.enable();
  this.update();
}

WTControl.prototype.enable = function()  {
  this.enablef(this.div);
  var t = this;
  this.div.onclick = function() { t.onclick(); };
  this.enabled = 1;
  this.set_title();
}

WTControl.prototype.disable = function() {
  this.enabled = 0;
  this.disablef(this.div);
  this.div.onclick = null;
  this.set_title();
}

WTControl.prototype.show = function() {
  this.style.display = '';
}

WTControl.prototype.hide = function() {
  this.style.display = 'none';
}

WTControl.prototype.is_visible = function() {
  return this.style.display != 'none';
}

WTControl.prototype.update = function() {
  this.div.innerHTML = exec_or_value(this.innerHTMLf, this, this.state);
  this.set_title();
}

WTControl.prototype.callback = function() {
  if (this.clickf)
    this.clickf(this.state);
}

WTControl.prototype.onclick = function() {
  this.state++;
  if (this.state >= this.n_states)
    this.state = 0;
  this.update();
  this.callback();
}

//WTControl.prototype.clear_title = function() {
// this.div.title = null;
//}

WTControl.prototype.set_title = function() {
  if (this.titlef)
    this.div.title = exec_or_value(this.titlef, this);
}

WTControl.prototype.set_state = function(state) {
  this.state = state;
  this.update();
}

WTControl.prototype.trigger = function(state) {
  this.state = state;
  this.update();
  this.callback();
}

WTControl.prototype.reset = function() {
  this.trigger(0);
}



	// for these guys, the action happens when the map calls initialize
function WTGControl(map, x, width, text0, text1, titlef, onclick, oncreate) {
  var c = new GControl(0, 0);
  c.initialize = function(map) {
    var w = new WTControl(map.getContainer(), 2,
	          function(d) { s = d.style;
				s.border          = '1px solid black'
				s.padding         = '0px 3px';
				s.backgroundColor = 'white';
				s.color           = 'black';
				s.fontSize        = '12px';
				s.fontFamily      = 'Arial,sans-serif';
				s.cursor          = 'pointer';
				s.width           = width + 'px';
				s.textAlign       = 'center';
	          },
		  null,  // for disable, tried  function(d) { d.style.color = '#606060'; },
		  function(i) { return i? text1 : text0; },
		  titlef,
		  onclick
            );

    if (oncreate)
      oncreate.call(w);
    return w.div;
  };
  map.addControl(c, new GControlPosition(G_ANCHOR_TOP_RIGHT, new GSize(x, 30)));
}

function answerbutton_enable(d) {
  d.className = 'answerbutton';
}


					//***************************** ICONS

	// plus sign is current cursor (e.g. lat/lon for next query)
var icon_plus = new GIcon();
icon_plus.image = "images/black-plus.png";
icon_plus.shadow = null;
icon_plus.iconSize = new GSize(24, 24);
icon_plus.shadowSize = new GSize(0, 0);
icon_plus.iconAnchor = new GPoint(12, 12);

	// X marks lat/lon of viewer of current result
var icon_x = new GIcon();
icon_x.image = "images/orchid-x.png";
icon_x.shadow = null;
icon_x.iconSize = new GSize(24, 24);
icon_x.shadowSize = new GSize(0, 0);
icon_x.iconAnchor = new GPoint(12, 12);
icon_x.infoWindowAnchor = new GPoint(12, 5);

	// summit markers
var default_type_icon = new GIcon();
default_type_icon.image = "images/nonsummit.png";
default_type_icon.shadow = "images/summit_shadow.png";
default_type_icon.iconSize = new GSize(12, 20);
default_type_icon.shadowSize = new GSize(22, 20);
default_type_icon.iconAnchor = new GPoint(6, 20);
default_type_icon.infoWindowAnchor = new GPoint(5, 1);

var type_icons = new Object;
type_icons['summit'] = new GIcon(default_type_icon);
type_icons['summit'].image = "images/summit.png";
type_icons['island'] = type_icons['summit'];

var default_type_color = '5959ff';
var type_colors = {
  summit: 'c60205',
  island: 'c60205'
};

var default_type_style = '';
var type_styles = {
   antenna: 'color: #5959ff'
};



	// answers (queries we've run)
var icon_my_private_answer = new GIcon(default_type_icon);
icon_my_private_answer.image = "images/google_mm_20_red.png";
icon_my_private_answer.shadow = "images/google_mm_20_shadow.png";

var icon_my_public_answer = new GIcon(icon_my_private_answer);
icon_my_public_answer.image = "images/mm_20_red_blue.png";

var icon_public_answer = new GIcon(icon_my_private_answer);
icon_public_answer.image = "images/google_mm_20_blue.png";



					//***************************** MISCELLANEOUS
/******************
function read_querystring_position() {
  var lat  = location.search.match('(?:\\?|&)' + 'lat'  + '=(-?[\\.\\d]+)');
  if (!lat)
     return 0;
  var lon  = location.search.match('(?:\\?|&)' + 'lon'  + '=(-?[\\.\\d]+)');
  if (!lon)
     return 0;
  var elev = location.search.match('(?:\\?|&)' + 'elev' + '=(-?[\\.\\d]+)');
  if (!elev)
     return 0;
  return [ read_angle(lat[1]), read_angle(lon[1]), elev[1] - 0 ];
}
*****************/

var meters_per_foot = .3048;
var meters_per_mile = 1609.344;

function feet_or_meters(x, is_html) {
  return current.use_metric? round0(x) + 'm' : round0(x / meters_per_foot) + (is_html? '&nbsp;' : ' ') + 'ft';
}

function miles_or_km(x, is_html) {
  return current.use_metric? round0(x/1000)            + (is_html? '&nbsp;' : ' ') + 'km'
			   : round0(x/meters_per_mile) + (is_html? '&nbsp;' : ' ') + (round0(x/meters_per_mile) == 1? 'mile' : 'miles');
}

function units_to_meters(x) {
  return current.use_metric? x : x * meters_per_foot;
}


var DEGREES_TO_RADIANS = Math.PI / 180.;
var EARTH_RADIUS = 6367447;

function bearing_and_range(lat0, lon0, lat1, lon1) {
	// rotate so viewer at zero longitude
  lon1 -= lon0;

	// about 11cm. prevents NaN values below
  if (Math.abs(lat1 - lat0) + Math.abs(lon1) < .000001)
     return [0, 0];

  lat0 *= DEGREES_TO_RADIANS;
  lat1 *= DEGREES_TO_RADIANS;
  lon1 *= DEGREES_TO_RADIANS;

  var sinlon1 = Math.sin(lon1);
  var coslon1 = Math.cos(lon1);
  var sinlat0 = Math.sin(lat0);
  var coslat0 = Math.cos(lat0);
  var sinlat1 = Math.sin(lat1);
  var coslat1 = Math.cos(lat1);

  var x = coslat1 * coslon1;
  var y = coslat1 * sinlon1;
  var z = sinlat1;

	// rotate about y axis so viewer at north pole
  var xx =  x * sinlat0 - z * coslat0;
  var yy =  y;
  var zz =  x * coslat0 + z * sinlat0;

  var theta = Math.PI - Math.atan2(yy, xx);
  var phi   = Math.acos(zz);
  return [ theta/DEGREES_TO_RADIANS, phi * EARTH_RADIUS];
}


function adjust_for_declination(d) {
  if (current.magnetic) {
    d -= result.declination;
    if (d < 0)
       d += 360;
    else if (d > 360)
       d -= 360;
  }
  return d;
}

function scroll_if_needed(e, div) {
  var i = e.offsetTop - div.scrollTop;
  if (i < 0)
    e.scrollIntoView();
  else if (i > parseInt(div.style.height) - 23)
    e.scrollIntoView(false);
}


/*
Trying to avoid scrollbars on the list_div
Due to silliness in CSS spec, on firefox (but interestingly
not on IE6 and IE7), once you ask for vertical scrollbar,
you have to specify a horizontal size. Further, layout gets
confused on firefox if you toggle the vertical scrollbar
on and off.
*/

function set_list_div_initial() {
  if (is_msie) {
    list_div.style.overflowY = 'auto';
  } else {
    list_div.style.overflow  = 'auto';
    list_div.style.height    = '650px';
  }
}

function set_list_div_style(min_width, show_vertical_scrollbar) {
  if (is_msie) {
    list_div.style.height    = show_vertical_scrollbar? '650px' : 'auto';
  } else {
    list_div.style.minWidth  = min_width;
  //list_div.style.overflowY = show_vertical_scrollbar? 'auto'    : 'visible';
  //list_div.style.height    = show_vertical_scrollbar? '650px'   : 'auto';
  //list_div.style.minWidth  = show_vertical_scrollbar? min_width : 'auto';
  }
}


// hack to add overlay of feature data (until we implement it right)

var icon_data = new GIcon();
icon_data.image = "images/blue-plus.png";
icon_data.shadow = null;
icon_data.iconSize = new GSize(13, 13);
icon_data.shadowSize = new GSize(0, 0);
icon_data.iconAnchor = new GPoint(6, 6);

function getnamesdata() {
  var b = map.getCenter();
  wt_async_request_array_of_lines('bin/namesdata.cgi?lat0=' + b.lat() + '&lon0=' +  b.lng(),
	      'NAMES DATA', function(a) {
		a._foreach(function(s) {
	          var t = s.match(/(-?[\d\.]+) (-?[\d\.]+) ([\-\d]+) \S+ \S+ (.*)/);
		  if (t)
		    map.addOverlay(new GMarker(new GLatLng(t[1], t[2]),
				{ icon: icon_data, clickable: false, title: feet_or_meters(t[3]) + ' ' + t[4] }));
		});});
}


/**************************
// simplify adding hacks (cf. http://www.elsewhere.org/journal/gmaptogpx)
// just need a bookmark that looks like 'javascript:addon("hack.js")'

function addon(s) {
  var script = document.createElement('script');
  script.src = s;
  document.getElementsByTagName('head')[0].appendChild(script);
}
*****************************/
