/**
 * ThereIs (for jQuery)
 * version: 0.9.1 (January 30, 2010S)
 * @requires jQuery v1.2 or later
 * @requires Google Maps API v3 or later
 *
 * Examples at http://scripts.paulkoppen.com/thereis
 *
 * Licensed under the MIT:
 *   http://www.opensource.org/licenses/mit-license.php
 *
 * Copyright 2009 Paul Koppen [ paul . koppen @ gmail . com ]
 *
 * Version History
 * 22 September 2009 - Paul Koppen - Initial version 
 * 30 January 2010 - Wessel Heringa - Fixed google api calls (lower_cased to CamelCased function calls)
 * 
 * Module Usage:
 * -------------
 *
 *  The default behavior renders the default location:
 *
 *   jQuery(document).ready(function(){
 *     // shows map following all default options
 *     jQuery('div.location').thereis();
 *   });
 *
 *  To render your specific location you can control (almost?) any attribute through options:
 *
 *   jQuery(document).ready(function(){
 *     // overwrite some options
 *     var home = google.maps.LatLng(,);
 *     var options = {
 *       map: {
 *         center: home,
 *         zoom: 10,
 *       },
 *       marker: {
 *         position: home,
 *       }
 *     };
 *     jQuery('#home').thereis(options);
 *   });
 *
 * Event Hooks:
 * ------------
 *
 *  The following hooks are defined:
 *    - thereis.change
 *    - thereis.geocoder.submit
 *
 *  To bind to such a hook, use:
 *
 *   $(document).bind('thereis.change', function(evt){
 *     // objects are exposed in evt.thereis
 *     alert(evt.thereis.map.get_center());
 *     alert(evt.thereis.marker.get_position());
 *     alert(evt.thereis.options.showgeocoder);
 *     // the 'thereis.change' hook also exposes the google event
 *     alert(evt.google);
 *   });
 *
 *  To change the default geocoder request handler, use:
 *
 *   $(document)
 *     .unbind('thereis.geocoder.submit')
 *     .bind('thereis.geocoder.submit', function(evt){
 *       // the 'thereis.geocoder.submit' hook adds a request attribute to the event
 *       alert(evt.thereis.request.address);
 *     });
 *
 */
(function($){
	/**
	 * ThereIs Maps Renderer
	 */
	$.fn.thereis = function(options) {
		var opts={}, x, y;
		for (x in $.thereis.defaults)
			if (typeof $.thereis.defaults[x] == 'object') {
				opts[x] = {};
				for (y in $.thereis.defaults[x])
					opts[x][y] = $.thereis.defaults[x][y];
			} else opts[x] = $.thereis.defaults[x];
		for (x in options)
			if (typeof options[x] == 'object')
				for (y in options[x])
					opts[x][y] = options[x][y];
			else
				opts[x] = options[x];
		
		return this.each(function(){
			
			function trigger_change(evt) {
				var event = $.extend($.Event('thereis.change'), {thereis:context, google:evt});
				$(document).trigger(event);
			}
			function trigger_geocoder_submit(evt) {
				var address = $(this).find(':text').eq(0).val();
				var event   = $.extend($.Event('thereis.geocoder.submit'), {
							thereis: $.extend({}, context, {
								request: $.extend({}, opts.geocoder.options, {address:address})}),
							jquery: evt});
				$(document).trigger(event);
				return false;
			}
			function release() {
				$(document).unbind('thereis.release', release);
				google.maps.event.clearInstanceListeners(map);
				google.maps.event.clearInstanceListeners(marker);
			}
			
			var $this    = $(this);
			var map      = new google.maps.Map(make_canvas(opts).appendTo($this).get(0), opts.map);
			var marker   = new google.maps.Marker(opts.marker);
			var geocoder = new google.maps.Geocoder();
			var context  = {$this:$this, map:map, marker:marker, geocoder:geocoder, options:opts};
		
			marker.setMap(map);
			if (opts.showgeocoder)
				make_geocoder(opts).appendTo($this).submit(trigger_geocoder_submit);
			
			google.maps.event.addListener(map, 'center_changed', trigger_change);
			google.maps.event.addListener(map, 'zoom_changed', trigger_change);
			google.maps.event.addListener(map, 'maptypeid_changed', trigger_change);
			google.maps.event.addListener(marker, 'position_changed', trigger_change);
			
			$(document).bind('thereis.release', release);
			
		});
	};
	
	/**
	 * HTML Generators
	 */
	function make_canvas(opts) {
		return $(['<div class="', opts.prefix, 'canvas"></div>'].join(''));
	}
	function make_geocoder(opts) {
		return $(['<form class="', opts.prefix, 'geocoder" method="get" action="#">',
					'<span class="', opts.prefix, 'searchtext">',
						'<input type="text" name="pos" /></span>',
					'<span class="', opts.prefix, 'searchbutton">',
						'<input type="submit" value="Search" /></span>',
					'</form>'].join(''));
	}
	
	/**
	 * Default Geocoder Handler
	 */
	function geocoder_onsubmit(evt) {
		evt.thereis.geocoder.geocode(evt.thereis.request, function(results, status){
			if (status == google.maps.GeocoderStatus.OK) {
				if (results.length) {
					evt.thereis.map.setCenter(results[0].geometry.location);
					evt.thereis.marker.setPosition(results[0].geometry.location);
					evt.thereis.map.fitBounds(results[0].geometry.viewport);
				}
				else alert(evt.thereis.options.geocoder.messages[
							google.maps.GeocoderStatus.ZERO_RESULTS]);
			} else alert(evt.thereis.options.geocoder.messages[status]);
		});
		return false;
	}
	$(document).bind('thereis.geocoder.submit', geocoder_onsubmit);
	
	/**
	 * Default Settings
	 */
	$.thereis = {
		defaults: {
			prefix: 'thereis_',
			map: {
				zoom: 19,
				center: new google.maps.LatLng(52.37381480, 4.90155197),
				mapTypeId: google.maps.MapTypeId.SATELLITE,	// ROADMAP, SATELLITE, HYBRID
				mapTypeControl: true,
				mapTypeControlOptions: {
					style: google.maps.MapTypeControlStyle.DROPDOWN_MENU
				},
				navigationControl: true,
				navigationControlOptions: {
					style: google.maps.NavigationControlStyle.SMALL
				}
			},
			marker: {
				position: new google.maps.LatLng(52.37382544, 4.90167133),
				draggable: true
			},
			geocoder: {
				options: {
					// bounds: ...
					// language: ...
					// country: ...
				},
				messages: {
					/**
					 * N.B. DO NOT CHANGE THESE, BUT THE GOOGLE STATUS CODE EQUIVALENTS! SEE #1#
					 */
					ZERO_RESULTS: 'No results matching your search query could be found.',
					OVER_QUERY_LIMIT: 'The maximum number of search requests has been reached.',
					REQUEST_DENIED: 'For some reason your search request was denied.',
					INVALID_REQUEST: 'The request was not understood.'
				}
			},
			showgeocoder: true
		}
	};
	
	/**
	 * #1#  Message keys are converted to their Google status code equivalents.
	 */
	for (statuscode in $.thereis.defaults.geocoder.messages)
		$.thereis.defaults.geocoder.messages[google.maps.GeocoderStatus[statuscode]] =
					$.thereis.defaults.geocoder.messages[statuscode];
	
})(jQuery);

