Number.prototype.ordinal = function () {
	return this + (
		(this % 10 == 1 && this % 100 != 11) ? 'st' :
		(this % 10 == 2 && this % 100 != 12) ? 'nd' :
		(this % 10 == 3 && this % 100 != 13) ? 'rd' : 'th'
	);
};
(function($) {
  
  /* usage:
  
	jQuery.callCongress('#container');
  
  */
  
  $.callCongress = function(containerSelector, name, settings) {
	settings = $.extend({
	  spinner: 'http://kaytwo.googlecode.com/svn/trunk/images/spinner.gif',
	  addressApi: 'http://api.democrats.org/address?callback=?',
	  addressApiKey: '08041906-c020-4eee-92e6-d7f48df70e45',
	  dataPrefix: '/page/smartproxy/www.barackobama.com/callcongress/data/',
	  marker: 'http://www.barackobama.com/displayer/images/generic.png',
	  markerShadow: 'http://www.barackobama.com/displayer/images/generic-shadow.png',
	  districtRegionsUrl: 'http://www.rdfabout.com/rdf/usgov/geo/us/',
	  govTrackRegionsUrl: 'http://www.govtrack.us/perl/wms/list-regions.cgi?dataset=http://www.rdfabout.com/rdf/usgov/congress/house/110&fields=area&format=json&json_callback=?&uri='
	}, settings);
	var stateMap = {"VA":"Virginia",
	  "ND":"North Dakota",
	  "NY":"New York",
	  "AL":"Alabama",
	  "RI":"Rhode Island",
	  "NE":"Nebraska",
	  "MN":"Minnesota",
	  "MD":"Maryland",
	  "HI":"Hawaii",
	  "DE":"Delaware",
	  "CO":"Colorado",
	  "PR":"Puerto Rico",
	  "WY":"Wyoming",
	  "MO":"Missouri",
	  "ME":"Maine",
	  "IA":"Iowa",
	  "MP":"Northern Mariana Islands",
	  "GU":"Guam",
	  "OR":"Oregon",
	  "OH":"Ohio",
	  "KY":"Kentucky",
	  "IL":"Illinois",
	  "AZ":"Arizona",
	  "TX":"Texas",
	  "TN":"Tennessee",
	  "NH":"New Hampshire",
	  "GA":"Georgia",
	  "MH":"Marshall Islands",
	  "SC":"South Carolina",
	  "IN":"Indiana",
	  "ID":"Idaho",
	  "SD":"South Dakota",
	  "PA":"Pennsylvania",
	  "OK":"Oklahoma",
	  "NJ":"New Jersey",
	  "MS":"Mississippi",
	  "MI":"Michigan",
	  "FL":"Florida",
	  "CT":"Connecticut",
	  "AR":"Arkansas",
	  "PW":"Palau",
	  "FM":"Federated States of Micronesia",
	  "AS":"American Samoa",
	  "WI":"Wisconsin",
	  "MT":"Montana",
	  "VI":"Virgin Islands",
	  "VT":"Vermont",
	  "NV":"Nevada",
	  "KS":"Kansas",
	  "CA":"California",
	  "DC":"District of Columbia",
	  "WV":"West Virginia",
	  "UT":"Utah",
	  "NM":"New Mexico",
	  "MA":"Massachusetts",
	  "WA":"Washington",
	  "NC":"North Carolina",
	  "LA":"Louisiana",
	  "AK":"Alaska"};
	
	var source = null;
	if ($.url) {
	  source = $.url.param('source');
	  name += '-' + source;
	}
	
	var container = $(containerSelector).eq(0);
	var inner = container.find('.callcongress').eq(0);
	var enterAddress = inner.children().eq(0);
	var results = inner.children().eq(1).hide();
	var report = inner.children().eq(2).hide();
	var thanks = inner.children().eq(3).hide();
	
	/* spinner */
	var spinner = $('<img class="cc-spinner" src="' + settings.spinner + '"/>');
	var nobr = $('<nobr />');
	
	/* submit processing */
	var startSubmit = function(form) {
	  var submit = form.find(':submit').eq(0);
	  submit.attr('disabled', true);
	  submit.wrapAll(nobr);
	  spinner.insertAfter(submit);
	  spinner.show();
	};
	var stopSubmit = function(form) {
	  var submit = form.find(':submit').eq(0);
	  spinner.hide();
	  submit.removeAttr('disabled');
	};
	
	/* error processing */
	var errorDiv = $('<div class="cc-error" />');
	var doError = function(msg, loc) {
	  errorDiv.html(msg);
	  errorDiv.insertBefore(loc);
	  errorDiv.slideDown();
	};
	var validateForm = function(form) {
	  var missing = [];
	  $.each(form.find('.required'), function() {
		var span = $(this);
		var label = span.parent();
		var field = label.parent().nextAll('.cc-input').eq(0).find(':input');
		console.debug('required: ' + label.text() + ' (' + field.attr('name') + ')');
		field.removeClass('cc-error-field');
		var value = field.val();
		if (value.match(/^[ \t]*$/)) {
		  missing.push('<label>' + label.text() + '</label>');
		  field.addClass('cc-error-field');
		}
	  });
	  if (missing.length > 0) {
		doError('The following fields are required: ' + missing.join('; '), form);
		stopSubmit(form);
		return false;
	  }
	  return true;
	}
	
	/* load google maps */
	google.load("maps", "2.x");
	var geocoder = null;
	var customIcon = null;
	var mapDiv = results.find('.cc-district-map-container').eq(0);
	
	/* step 1 processing */
	var addressForm = enterAddress.find('form').eq(0);
	addressForm.bind('submit', function(e) {
	  e.preventDefault();
	  startSubmit(addressForm);
	  errorDiv.slideUp();
	  var line1Input = addressForm.find('input[name=line1]').eq(0);
	  var line2Input = addressForm.find('input[name=line2]').eq(0);
	  var line1 = line1Input.val();
	  var line2 = line2Input.val();
	  console.debug('line1 = ' + line1);
	  console.debug('line2 = ' + line2);
	  if(!validateForm(addressForm)) {
		return false;
	  }
	  $.getJSON(settings.addressApi, {'key': settings.addressApiKey, 'line1': line1, 'line2': line2}, function(address) {
		console.dir(address);
		if (!address.cd || address.cd == '') {
		  doError('No congressional district found for that address &mdash; make sure to enter your full address', addressForm);
		  stopSubmit(addressForm);
		  return false;
		}
	  
		/* populate step 2 */
		var addrStr = address.addr + ', ' + address.city + ', ' + address.state + ' ' + address.zip;
		console.debug(addrStr);
		var resultAddress = results.find('.cc-address-text').eq(0);
		resultAddress.text(addrStr);
		var changeAddress = results.find('.cc-change-address').eq(0);
		changeAddress.bind('click', function(e) {
		  e.preventDefault();
		  enterAddress.slideDown(function() {
			enterAddress.find(':input').eq(0).select();
		  });
		  changeAddress.hide();
		})
		var districtHeading = results.find('.cc-heading').eq(0);
		var districtStr = stateMap[address.state] + "'s " + parseInt(address.cd).ordinal() + ' district';
		districtHeading.text(districtStr);
		
		/* generate district map */
		var makeMap = function(atLarge) {
		  if (!google.maps) { return; }
		  if (!geocoder) { geocoder = new google.maps.ClientGeocoder(); }
		  if (!customIcon) {
			customIcon = new google.maps.Icon();
			customIcon.image = settings.marker;
			customIcon.shadow = settings.markerShadow;
			customIcon.iconSize = new google.maps.Size(24, 35);
			customIcon.shadowSize = new google.maps.Size(44, 35);
			customIcon.iconAnchor = new google.maps.Point(12, 35);
			customIcon.infoWindowAnchor = new google.maps.Point(12, 1);
		  }
		  console.debug('geocoding: ' + addrStr);
		  geocoder.getLatLng(addrStr, function(latlng) {
			var map = new google.maps.Map2(mapDiv[0]);
			map.addControl(new google.maps.SmallZoomControl());
			var uri = settings.districtRegionsUrl + address.state.toLowerCase();
			var number = address.cd;
			if (atLarge) {
			  number = '0';
			} else {
			  uri +=  '/cd/110/' + address.cd;
			}
			var url = settings.govTrackRegionsUrl + uri;
			console.log(url);
			jQuery.getJSON(url, function(data) {
			  var maxMatch = 0;
			  jQuery.each(data, function(i, item) {
				if (item[0] == uri && item[1] > maxMatch) {
				  maxMatch = item[1];
				}
			  });
			  console.log('area = ' + maxMatch);
			  var zoomLevel = 14 - Math.round(Math.log(Math.sqrt(maxMatch))/Math.log(2));
			  console.log('zoomLevel = ' + zoomLevel);
			  map.setCenter(latlng, zoomLevel);
			  var WMS_URL = 'http://www.govtrack.us/perl/wms-cd.cgi?';
			  var G_MAP_LAYER_FILLED = createWMSTileLayer(WMS_URL, "cd-filled,district=" + address.state + number, null, "image/gif", null, null, null, .25);
			  var G_MAP_LAYER_OUTLINES = createWMSTileLayer(WMS_URL, "cd-outline,district=" + address.state + number, null, "image/gif", null, null, null, .66, "Data from GovTrack.us");
			  var G_MAP_OVERLAY = createWMSOverlayMapType([G_NORMAL_MAP.getTileLayers()[0], G_MAP_LAYER_FILLED, G_MAP_LAYER_OUTLINES], "Overlay");
			  map.addMapType(G_MAP_OVERLAY);
			  map.setMapType(G_MAP_OVERLAY);
			  var m = new google.maps.Marker(latlng, {icon: customIcon});
			  map.addOverlay(m);
			});
		  });
		}; /* end makeMap */
	  
		/* load rep data */
		var districtDataFile = settings.dataPrefix + address.state + address.cd + '.json';
		var stateDataFile = settings.dataPrefix + address.state + '.json';
		console.debug(stateDataFile);
		var populateRep = function(data, cls) {
		  var row = $('tr.' + cls);
		  row.find('.cc-rep-name').text(data.name);
		  row.find('.cc-phone-number').text(data.phone);
		  $('.' + cls).show();
		};
		$('.cc-sen').show();
		var houseResult = null;
		var senResult = null;
		$.ajax({
		  type: 'GET',
		  url: stateDataFile,
		  dataType: 'json',
		  success: function(stateData) {
			console.dir(stateData);
			console.debug(districtDataFile);
			var sen1 = stateData[0];
			var sen2 = stateData[1];
			if (sen1) {
			  populateRep(sen1, 'cc-sen1');
			} else {
			  $('.cc-sen1').hide();
			}
			if (sen2) {
			  populateRep(sen2, 'cc-sen2');
			} else {
			  $('.cc-sen2').hide();
			}
			senResult = true;
			if (houseResult == false) {
			  enterAddress.slideUp();
			  changeAddress.show();
			  openResults(false);
			}
		  },
		  error: function() {
			senResult = false;
			if (houseResult == false) {
			  doError('No representatives found for that address', addressForm);
			  stopSubmit(addressForm);
			} else {
			  $('.cc-sen').hide();
			}
		  }
		});
		$.ajax({
		  type: 'GET',
		  url: districtDataFile,
		  dataType: 'json',
		  success: function(districtData) {
			console.dir(districtData);
			var rep = districtData;
			houseResult = true
			if (rep.atLarge) {
			  districtStr = stateMap[address.state] + "'s at-large district";
			  districtHeading.text(districtStr);
			}
			enterAddress.slideUp();
			changeAddress.show();
			populateRep(rep, 'cc-house');
			openResults(rep.atLarge);
		  },
		  error: function() {
			/* no district data */
			houseResult = false;
			if (senResult == false) {
			  doError('No representatives found for that address', addressForm);
			  stopSubmit(addressForm);
			} else if (senResult == true) {
			  $('.cc-house').hide();
			  enterAddress.slideUp();
			  changeAddress.show();
			  openResults(false);
			} else {
			  $('.cc-house').hide();
			}
		  }
		});
		var openResults = function(atLarge) {
		  stopSubmit(addressForm);
		  results.slideDown(function() {
			makeMap(atLarge);
		  });
		  
		  /* step 2 processing */
		  results.find('.cc-submit input').eq(0).bind('click', function(e) {
			e.preventDefault();
			startSubmit(results);
			if (Tracking && Tracking.pageTracker) {
			  var url = '/action/callcongress/' + name + '/call/' + address.state + '/' + address.cd;
			  console.debug('tracking pageview: ' + url);
			  Tracking.pageTracker._trackPageview(url);
			}
			/* populate step 3 -- report */
			report.find('input[name=addr1]').attr('value', address.addr);
			report.find('input[name=city]').attr('value', address.city);
			report.find('input[name=state_cd]').attr('value', address.state);
			report.find('input[name=zip]').attr('value', address.zip);
			report.find('input.cc-input-cd').attr('value', address.state + address.cd);
			var reportForm = report.find('form').eq(0);
			if (source) {
			  reportForm.attr('action', reportForm.attr('action') + '?source=' + source);
			}
			enterAddress.hide();
			changeAddress.hide();
			results.slideUp();
			report.slideDown();
			reportForm.find(':input:visible').eq(0).focus();
			reportForm.bind('submit', function(e) {
			  e.preventDefault();
			  startSubmit(reportForm);
			  errorDiv.slideUp();
			  if(!validateForm(reportForm)) {
				return false;
			  }
			  var data = reportForm.formSerialize();
			  console.debug('submitting: ' + data);
			  /* ajax submit */
			  $.ajax({
				url: reportForm.attr('action'),
				type: reportForm.attr('method'),
				data: data,
				dataType: null,
				error: function(msg) {
				  console.debug('error: ' + msg);
				},
				complete: function (XMLHttpRequest, textStatus) {
				  /* submit complete */
				  report.slideUp();
				  
				  /* show step 4 -- thanks */
				  thanks.slideDown();
				}
			  });
			});
		  });
		};
	  });
	});
  }
  
})(jQuery);