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


			///////////// UTILS /////////////////

var is_msie = navigator.userAgent.toLowerCase().indexOf('msie') != -1;

var select_add_to_end = is_msie? (function(sel, o) { sel.add(o); }) : (function(sel, o) { sel.add(o, null); });

if (!String.prototype.localeCompare)
  String.prototype.localeCompare = function(s) { return this < s? -1 : this > s? 1 : 0; };


function WTBrowserCompatible() {
  var s = navigator.userAgent.toLowerCase();
  if (s.match(/(firefox|iceweasel)\/(3|2|1\.5)/i))
    return 1;
  return 0;
}


	// Possibly the right way to do inheritance cf. http://www.kevlindev.com/tutorials/javascript/inheritance/
function derive(newclass, base) {
  function xxx() {}
  xxx.prototype      = base.prototype;
  newclass.prototype = new xxx();
}

Array.prototype._foreach = function(f) { for (var __i = 0; __i < this.length; __i++) f(this[__i]); };
Array.prototype._while   = function(f) { for (var __i = 0; __i < this.length; __i++) if (!f(this[__i])) return false; return true; };


function read_cookie(c) {
  var s = document.cookie.match(new RegExp(c + '=([^;]*)'));
  return s && s[1]? s[1] : null;  
}

function set_cookie(c, v, days) {
  days = days || 365;
  document.cookie = c + '=' + v + ';expires=' + new Date((new Date()).getTime() + days * 24 * 60 * 60 * 100).toGMTString();
}


function round0(x) {
  return Math.round(x);
}

function round1(x) {
  return Math.round(x * 10)/10;
}

function round2(x) {
  return Math.round(x * 100)/100;
}

function round3(x) {
  return Math.round(x * 1000)/1000;
}

function round4(x) {
  return Math.round(x * 10000)/10000;
}

function round5(x) {
  return Math.round(x * 100000)/100000;
}

function round6(x) {
  return Math.round(x * 1000000)/1000000;
}


function radio_clear(r) {
  for (var i = 0; i < r.length; i++)
    r[i].checked = 0;
}

function radio_set(r, v) {
  for (var i = 0; i < r.length; i++)
    if (r[i].value == v) {
      r[i].checked = 1;
      return 1;
    }
  return 0;
}

function radio_get(r) {
  for (var i = 0; i < r.length; i++)
    if (r[i].checked)
      return r[i].value;
  return null;
}

function encodeURI_more(s) {
  s = encodeURI(s);
  s = s.replace(/\&/g, '%26');
  s = s.replace(/\=/g, '%3D');
  s = s.replace(/\+/g, '%2B');
  return s;
}

function exec_or_value(f, o) {
  if (typeof f != 'function')
    return f;
		// arguments.slice(2) doesn't work
  var a = [];
  for (var i = 2; i < arguments.length; i++)
    a.push(arguments[i]);
  return f.apply(o, a);
}

function word_param(k) {
  var a = location.search.match('(?:\\?|&)' + k + '=(\\w+)');
  return a && a[1];
}

function words_param(k) {
  var a = location.search.match('(?:\\?|&)' + k + '=([\\+\\w]+)');
  return a && a[1]? a[1].split('+') : [];
}

function param(k) {
  var a = location.search.match('(?:\\?|&)' + k + '=(.*?)(?:&|$)');
  return a && a[1];
}

function angle_param(k) {
  var a = location.search.match('(?:\\?|&)' + k + '=([\\+\\-]?[\\d\\.]+)');
  return a && a[1];
}


function twodigits(x) {
  return x < 10? '0' + x : x;
}

function default_timezone() {
  var d = new Date;
  var m = d.getTimezoneOffset();
  var s = (m > 0? '-' : '+');
  m = Math.abs(m);
  return s + twodigits(Math.floor(m / 60)) + twodigits(m - 60 * Math.floor(m / 60));
}


			///////////// ANGLES /////////////////

function read_angle(s) {
    // leading spaces and + or -
    var a = s.match(/^\s*([\+\-]?)(.*)/);
    s = a[2];
    var sign = a[1] == '-'? -1 : 1;
    
    // then the rest all digits or spaces or decimal point
    if (s.search(/[^\d\.\s]/) != -1)
        return null;

    // only one decimal point, and it must be on last element
    if (s.search(/\..*(\s|\.)/) != -1)
        return null;

    var dms = s.split(/\s+/);

    if (dms.length >= 3)
	return sign * ((dms[0] - 0) + ((dms[1] - 0) + (dms[2] - 0) / 60) / 60);
    else if (dms.length == 2)
	return sign * ((dms[0] - 0) + (dms[1] - 0) / 60);

    var l = s.indexOf('.');
    var frac;
    if (l < 0) {
      frac = 0;
      l = s.length;
    } else {
      frac = s.substring(l) - 0;
      s = s.substring(0, l);
    }

    if (l < 4)				// 44 or 108.  NOTE: 108 is 108 deg, not 1 deg 8'
	return sign * (s - 0 + frac);
    else if (l < 6)			// 4423 10804
	return sign * ((s.substring(0, l-2) - 0) + ((s.substring(l-2, l) - 0) + frac) / 60);
    else
	return sign * ((s.substring(0, l-4) - 0) + ((s.substring(l-4, l-2) - 0) + ((s.substring(l-2, l) - 0) + frac) / 60) / 60);
}

// HACK: mode is number of parts - 1, e.g. 0 for DD, 1 for 'DDMM', 2 for 'DDMMSS'
function format_angle(x, mode, pos_char, neg_char, is_html) {
  var a = expand_angle(Math.abs(x), mode);
  var s = '';
  var symbols = [is_html? '&deg;' : '', "'", '"'];
  var space = is_html? '&nbsp;' : ' ';
  for (var i = 0; i <= mode; i++)
    s += (i == 0? '' : space) + a[i] + symbols[i];
  if (x < 0)
    if (neg_char)
      s += space + neg_char;
    else
      s = '-' + s;
  else if (pos_char)
   s += space + pos_char;

  return s;
}

function expand_angle(x, mode) {
  if (mode == 0)
    return [round6(x)];
  var d = Math.floor(x);
  x = 60 * (x - d);
  if (mode == 1)
    return [d, round4(x)];
  var m = Math.floor(x);
  x = 60 * (x - m);
  return [d, m, round2(x)];
}

function format_lat(x, mode, is_html) {
    return format_angle(x, mode, 'N', 'S', is_html);
}

function format_lon(x, mode, is_html) {
    return format_angle(x, mode, 'E', 'W', is_html);
}

function format_latlon(lat, lon, mode, is_html) {
  return    format_lat(lat, mode, is_html)
          + (is_html? '&nbsp;&nbsp;' : '  ')
          + format_lon(lon, mode, is_html);
}


			///////////// REQUESTS /////////////////

			//////////////////////////////////////// WT SPECIFIC

function signup() {
  var email = prompt("Give us an email address and\nwe'll send you news about the site", '');
  if (email)
    wt_async_request('/bin/subscribe.cgi?email=' + encodeURI_more(email), 'SUBSCRIBE',
						function(s) { alert(s.replace(/\n/g,'') + ' subscribed.  Thanks.') });
}

function results_file(id, filename) {
  return '/results/' + id + '/' + filename;
}

// DATA format: status lat lon elev elev-above-ground queued_time start_time end_time is_public declination name
var DATA_PUBLIC_INDEX = 8;
var DATA_NAME_INDEX = 11;

			//////////////////////////////////////// REQUESTS

// BUG: make all requests asynchronous, so browser doesn't get hung up if server hangs
//      To do so we could implement something like a list of requests, followed by a
//	function to call once they're all complete, and maybe a sempahore and a cancel routine


function _wt_request(url, error_label) {
  var request  = GXmlHttp.create();
  request.open("GET", url, 0);
  request.send(null);
  var o = parse_response(request);
  if (!o.ok && error_label)
    alert(_wt_request_error_message(error_label, o));
  return o;
}


function _wt_async_request(url, error_label, callback) {
  var request  = GXmlHttp.create();
  request.open("GET", url, true);
  request.onreadystatechange = function() {
    if (request.readyState != 4)
      return;
    var o = parse_response(request);
    if (!o.ok && error_label)
      alert(_wt_request_error_message(error_label, o));
    callback(o);
  }
  request.send(null);
}


function _wt_request_error_message(error_label, o) {
   return error_label + ': '
	  + (  o.app_status_message? o.app_status_message
	     : o.http_status_message && o.http_status_message.toLowerCase() != 'ok'? o.http_status_message
             : 'request error');
}


function wt_request(url, error_label) {
  var o = _wt_request(url, error_label);
  return o.response;
}


function wt_async_request(url, error_label, callback) {
//XXXXXXXXXXX  _wt_async_request(url, error_label, function(o) { if (o.ok && o.response) callback(o.response); });
  _wt_async_request(url, error_label, function(o) { if (o.ok) callback(o.response); });
}
  

function wt_request_array(url, error_label, test) {
  var o = _wt_request(url, error_label);
  if (!o.ok)
    return null;

		// get rid of final NL so we don't have extra field
  o.response_array = o.response.replace(/\n$/, '').replace(/\n/g, ' ').split(' ');
  if (test && !test(o.response_array)) {
    if (error_label)
      alert(error_label + ': invalid response');
    return null;
  }

  return o.response_array;
}

function wt_async_request_array(url, error_label, test, callback) {
  _wt_async_request(url, error_label, function(o) {
    if (!o.ok)
      return;
		// get rid of final NL so we don't have extra field
    o.response_array = o.response.replace(/\n$/, '').replace(/\n/g, ' ').split(' ');
    if (test && !test(o.response_array)) {
      if (error_label)
        alert(error_label + ': invalid response');
      return;
    }
    if (o.response_array)
      callback(o.response_array)
  });
}

function wt_request_array_of_lines(url, error_label) {
  var s = wt_request(url, error_label);
  if (s == null)
    return null;
	// pop off last element, which will be blank (assuming response ends with \n)
  var a = s.split('\n');
  if (a.length)
    a.pop();
  return a;
}

function wt_async_request_array_of_lines(url, error_label, callback) {
	// pop off last element, which will be blank (assuming response ends with \n)
  wt_async_request(url, error_label, function(s) { var a = s.split('\n'); if (a.length) a.pop(); callback(a); });
}

function parse_response(request) {
  var o = {ok: 0,
	   response: null,
	   http_status_code: null,
	   http_status_message: null,
	   app_status_code: null,
	   app_status_message: null};

  o.http_status_code    = request.status;
  o.http_status_message = request.statusText;
  if (request.status != 200)
    return o;

  var s;
  var a;
  try { s = request.getResponseHeader('X-App-Status'); } catch(e) {}
  if (s && (a = s.match(/^(\d\d\d)\s+(.+)/))) {
    o.app_status_code    = a[1];
    o.app_status_message = a[2];
    if (o.app_status_code != 200)
      return o;
  }

  o.ok             = 1;
  o.response       = request.responseText;

  return o;
}


/***********
function wt_async_request_with_error_callback(url, callback, error_callback) {
  _wt_async_request(url, '', function(o) { if (o.ok && o.response) callback(o.response); else error_callback(o); });
}

function wt_async_request_array_with_error_callback(url, callback, error_callback) {
  _wt_async_request(url, '',
    function(o) {
      if (o.ok && o.response) {
        o.response_array = o.response.replace(/\n$/, '').replace(/\n/g, ' ').split(' ');
        callback(o.response_array);
      } else {
        error_callback(o);
      }
    }
  );
}
*************/


			//////////////////////////////////////// GEOCODING

var geocoder = null;
function geocode(s, f) {
  var p;
  if (p = geocode_latlon(s)) {
    f(p, 0);
    return;
  }

  if (!geocoder)
    geocoder = new GClientGeocoder();

  geocoder.getLatLng(s, function(l) {
    if (l)
      f(l, 1);
    else
      alert(s + " not found");
  });
}

function geocode_latlon(s) {
	// 3/2009 Google's geocoder now seems to respond to lat/lon with
        //        the point on a road closest to it, which isn't what we want
  var a = s.match(/^([\-\+]?[\d\.]+)\s*([ns]?)[\s\,]+([\-\+]?[\d\.]+)\s*([ew]?)$/i);

  if (a && a[1] && a[3]) {
    var lat = a[1] - 0;
    if (a[2] == 's' || a[2] == 'S')
	lat = -lat;
    var lon = a[3] - 0;
    if (a[4] == 'w' || a[4] == 'W')
	lon = -lon;
    return new GLatLng(lat, lon);
  }
  return null;
}
