
	// -------------------------------------------------------------
	// Freestlyer::Field::GoogleMap
	// -------------------------------------------------------------
	// Copyright 2007-2009 Datalink Internet Systems Pty Ltd
	// www.datalink.com.au
	// -------------------------------------------------------------
	
	/**
	 * FsFieldGoogleMap
	 *
	 * Provides a Freestyler field that displays an embeddable Google
	 * Map and allows the user to click on the map to place and remove
	 * map points.  These points are then stored back into Freestyler
	 * as coordinate pairs.
	 *
	 * @author Scott Davey
	 * @since 5.1.6
	 */

	/***************************************************************
	 * Constructor
	 */
	 
	function FsFieldGoogleMap(id, prefix, fieldName, startLat, startLng, zoom, editable, addressSuffix, mapType)
	{

		// determine whether Google Map has loaded, and disable the class if not.
		this.enabled = Boolean(typeof GMap2 != "undefined" && GBrowserIsCompatible());
		if (!this.enabled) return;
	
		this.mapId = id;
		this.prefix = prefix;
		this.fieldName = fieldName;
		this.startLat = startLat;
		this.startLng = startLng;
		this.startZoom = zoom;
		this.editable = editable;	
		this.points = Array();
		this.pointsCounter = 0;
		this.geocoder = null;
		this.addressSuffix = addressSuffix;
		if (mapType) {
			this.mapType = mapType;
		} else {
			this.mapType = G_NORMAL_MAP;
		}
		
			
	}
	new FsFieldGoogleMap; // instantiate one so prototyping works
	var FsFieldGoogleMapTmpInstance; // this is used for address lookup purposes and must be global :-(
	
	/***************************************************************
	 * Class attributes
	 */
	 
	FsFieldGoogleMap.prototype.enabled;			 // A boolean flag whether or not it is enabled (false if GMap not loaded)
	FsFieldGoogleMap.prototype.mapId;            // The ID for the map DIV
	FsFieldGoogleMap.prototype.prefix;           // The prefix for all input boxes and bound HTML elements
	FsFieldGoogleMap.prototype.fieldName;		 // The Freestyler field name
	FsFieldGoogleMap.prototype.startLat;         // The starting lattitude
	FsFieldGoogleMap.prototype.startLng;         // The starting longitude
	FsFieldGoogleMap.prototype.startZoom;        // The starting zoom level
	FsFieldGoogleMap.prototype.editable;		 // true if editable, false if readonly
	FsFieldGoogleMap.prototype.map;              // the map object
	FsFieldGoogleMap.prototype.geocoder;         // the geocoder object
	FsFieldGoogleMap.prototype.points;           // an array of all points
	FsFieldGoogleMap.prototype.pointsCounter;    // the counter of points in the array
	FsFieldGoogleMap.prototype.geocoder;         // a geocoder object to look up addresses
	
					
	/***************************************************************
	 * Method: init
	 *
	 * Call this method to initialise a new instance of this field.
	 */
	 
	FsFieldGoogleMap.prototype.init = function ()
	{
		if (!this.enabled) return;
					
		// Create a new GMap object, add a Type control and a
		// Map control, and set the centre and zoom.
		this.map = new GMap2(document.getElementById(this.mapId));
		this.map.addControl(new GSmallMapControl());
		this.map.addControl(new GMapTypeControl());
		this.map.addMapType(this.mapType);
		this.map.setCenter(new GLatLng(this.startLat,this.startLng), this.startZoom, G_NORMAL_MAP);

		// set up the geocoder
		this.geocoder = new GClientGeocoder();
		this.geocoder.setBaseCountryCode('au');
		

		// Add a pointer to *this* so we can navigate back here from the map object
		this.map.fsFieldGoogleMap = this; 
						
		// Add a click listener that adds and removes markers
		GEvent.addListener(this.map, "click", this.clickListener);
		GEvent.addListener(this.map, "moveend", this.moveListener);
		GEvent.addListener(this.map, "zoomend", this.zoomListener);
		
	}; // end Method init

	
	/***************************************************************
	 * Method: setEditable
	 *
	 * Sets the editable status, allowing users to add/remove points
	 *
	 * @param editable true if editable, false if read only
	 */
	FsFieldGoogleMap.prototype.setEditable = function (editable)
	{
		if (!this.enabled) return;

		this.editable = editable;
	}
	 
	
	
	/***************************************************************
	 * Method: showAddress
	 *
	 * Displays the provided address on the map.
	 * 
	 * This method is bound to a fixed textbox and selectbox identified
	 * by the instance prefix with 'address' and 'region' appended
	 * respecively.
	 */
	 
	FsFieldGoogleMap.prototype.showAddress = function ()
	{
		if (!this.enabled) return;
		
		// bind to the form elements
		var addressField = document.getElementById(this.prefix + 'address');
		var regionField = document.getElementById(this.prefix + 'region');

		if (typeof(addressField) == 'undefined' || typeof(regionField) == 'undefined') {
			alert("Freestyler Field Google Maps field is not set up correctly");
		}

		var address = '';
		if (addressField) address = addressField.value;
		if (regionField) address = address + ', ' + regionField.options[regionField.selectedIndex].value;
		if (this.addressSuffix) address = address + ', ' + this.addressSuffix;
	
		if (this.geocoder) {
			// setting a global temporarily so we can get access to it in the callback function.
			FsFieldGoogleMapTmpInstance = this;
		
			this.geocoder.getLatLng(
				address,
				function(point) {
					// get the map instance
					var map = FsFieldGoogleMapTmpInstance.map;
					
					if (!point) {
						alert(address + " not found");
					} else {
						map.setCenter(point, 13);
						var marker = new GMarker(point);
						map.addOverlay(marker);
						marker.openInfoWindowHtml(address);
					}
					
					// we're finished with the global, so set it to null
					FsFieldGoogleMapTmpInstance = null;
				}
			);
		} // end if geocoder

	}; // end Method showAddress

	/***************************************************************
	 * Method updateCoords
	 *
	 * Updates a HTML input field with the coordinates of all
	 * markers in the map.  The data in the field can then be
	 * submitted in a form as usual.  It is typical for the input
	 * field to be hidden.
	 *
	 * This method is bound to an input field of id 'fs_field_googlemap_*_coords
	 * where * is the name of the field.  The field /name/ is the freestyler field name.
	 *
	 * The value inserted into the textbox is a pipe-separated (|) series
	 * of comma-separated (,) coordinate pairs.
	 */
	 
	FsFieldGoogleMap.prototype.updateCoords = function ()
	{
		if (!this.enabled) return;
	
		var str = "";
		for(i=1;i <= this.pointsCounter;i++) {
			point = this.points[i];
			if (typeof point != "undefined" && point != null) {
				point = point.getPoint();
				str += point.lat() + ", " + point.lng() + " | ";
			}
		}

		str += this.getSettings();
		
		document.getElementById('fs_field_googlemap_' + this.fieldName + '_coords').value = str;
		
	}; // end method updateCoords

	/***************************************************************
	 * Method: clicklistener
	 *
	 * This method is registered to the onClick event of the map and it
	 * has *this* reassigned to be the map object rather than
	 * fsFieldGoogleMap object.  (To access the fsFieldGoogleMap object,
	 * use this.fsFieldGoogleMap instead)
	 *
	 * The params are supplied by GMap as the overlay or the point
	 * clicked.  An overlay is a graphic element on the map, and a
	 * point is a place where no overlay exists.  So, passing a point
	 * means 'create an overlay' and passing an overlay means 'remove
	 * this overlay'
	 *
	 * @param overlay The overlay on the map just clicked
	 * @param point The point on the map just clicked where no overlay exists
	 */
	 
	FsFieldGoogleMap.prototype.clickListener = function (overlay, point)
	{
	
		if (!this.fsFieldGoogleMap.enabled) return;
		if (!this.fsFieldGoogleMap.editable) return;
	
		if (overlay) {
			this.fsFieldGoogleMap.removePoint(overlay);

		} else {
			// add the point
			this.fsFieldGoogleMap.addPoint(point);
									
			
		}
	}


	/****************************************************************
	 * Method: moveListener
	 * 
	 * This is registered against the 'moveend' event, and updates the
	 * map state when the move is completed.
	 */
	FsFieldGoogleMap.prototype.moveListener = function ()
	{
		if (!this.fsFieldGoogleMap.enabled) return;
		if (!this.fsFieldGoogleMap.editable) return;

		this.fsFieldGoogleMap.updateCoords();
		
	}
	
	/*****************************************************************
	 * Method:zoomListener
	 * 
	 *  This is registered against the 'zoom' event, and updates the
	 *  map state when the zoom has completed.
	 */
	FsFieldGoogleMap.prototype.zoomListener = function (oldLevel, newLevel)
	{
		if (!this.fsFieldGoogleMap.enabled) return;
		if (!this.fsFieldGoogleMap.editable) return;
		
		this.fsFieldGoogleMap.updateCoords();

	}
	

	
	
	/***************************************************************
	 * Method: addPoint
	 *
	 * Adds a point onto the map
	 *
	 * @param point a GPoint object
	 */
	FsFieldGoogleMap.prototype.addPoint = function (point)
	{
		if (!this.enabled) return;
	
		// Get the new point index
		this.pointsCounter++;
			
		// make the new point and add it to the index
		this.points[this.pointsCounter] = new GMarker(point);
			
		// add a custom field to the point so we can navigate back to
		// its position in the index if we need to remove it again.
		this.points[this.pointsCounter].fsFieldGoogleMapPointIndex = this.pointsCounter;
		
		// Add the point to the map
		this.map.addOverlay(this.points[this.pointsCounter]);
		
		// update the coords field.
		this.updateCoords();

	}
	
	/***************************************************************
	 * Method: addPointByCoord
	 *
	 * Adds a point onto the map using lat,lng coords
	 *
	 * @param lat The latitude
	 * @param lng The longitude
	 */
	FsFieldGoogleMap.prototype.addPointByCoord = function (lat,lng)
	{
		if (!this.enabled) return;
	
		point = new GLatLng(lat,lng);
		this.addPoint(point);
	
	}

	/***************************************************************
	 * Method: removePoint
	 *
	 * Removes a point from the map, updating the field's values
	 * @param overlay
	 */
	FsFieldGoogleMap.prototype.removePoint = function(overlay)
	{
		if (!this.enabled) return;
	
		// remove the point from the index
		this.points[overlay.fsFieldGoogleMapPointIndex] = null; // remove the point
		
		
		// Remove the marker from the map
		this.map.removeOverlay(overlay);
		
		// update the coords field
		this.updateCoords();	
	}

	/***************************************************************
	* Method: getSettings
	*
	* Returns a string representing the current zoom level, lat and
	* long.
	*/
	FsFieldGoogleMap.prototype.getSettings = function()
	{
		if (!this.enabled) return;
	
		point = this.map.getCenter();
		lat = point.lat();
		lng = point.lng();
		zoom = this.map.getZoom();

		return lat + ',' + lng + ',' + zoom;
		
	}

	// -------------------------------------------------------------
	// End Freestlyer::Field::GoogleMap
	// -------------------------------------------------------------
				
