function GoogleMapsLocationSelector(options)
{
	var searchURL = options.searchURL != undefined ? options.searchURL : '';
	var updateURL = options.updateURL != undefined ? options.updateURL : '';
	var selectionLimit = options.selectionLimit != undefined ? options.selectionLimit : 1;
	var singleSelection = options.singleSelection != undefined ? options.singleSelection : false;
	var required = options.required != undefined ? options.required : false;
	var centerLatitude = options.centerLatitude != undefined ? options.centerLatitude : 50.8462807;
	var centerLongitude = options.centerLongitude != undefined ? options.centerLongitude : 4.3547273;
	var initialZoom = options.initialZoom != undefined ? options.initialZoom : 4;
	var initialQuery = options.initialQuery != undefined ? options.initialQuery : '';
	var language = options.language != undefined ? options.language : '';
	var geocodeQueryDelay = options.geocodeQueryDelay != undefined ? options.geocodeQueryDelay : 500;

	var callback = options.callback != undefined ? options.callback : null;
	
	var dialogTilteText = options.dialogTilteText != undefined ? options.dialogTilteText : 'Select item';
	var searchFieldText = options.searchFieldText != undefined ? options.searchFieldText : 'Query';
	var searchButtonText = options.searchButtonText != undefined ? options.searchButtonText : 'Search';
	var okButtonText = options.okButtonText != undefined ? options.okButtonText : 'OK';
	var closeButtonText = options.closeButtonText != undefined ? options.closeButtonText : 'Close';
	var notFoundButtonText = options.notFoundButtonText != undefined ? options.notFoundButtonText : '';
	var addToListText = options.addToListText != undefined ? options.addToListText : 'Add to list';
	var selectedListText = options.selectedListText != undefined ? options.selectedListText : 'Selected items';
	var selectedListEmptyText = options.selectedListEmptyText != undefined ? options.selectedListEmptyText : 'No selected items';
	var noResultsText = options.noResultsText != undefined ? options.noResultsText : 'No results for the query';
	var deleteButtonText = options.deleteButtonText != undefined ? options.deleteButtonText : 'Delete';
	var limitExceedMsg = options.limitExceedMsg != undefined ? options.limitExceedMsg : 'You have reached the selection limit';
	var itemAlreadyAddedMsg = options.itemAlreadyAddedMsg != undefined ? options.itemAlreadyAddedMsg : 'Selected item is already in the list';
	var selectionRequiredMsg = options.selectionRequiredMsg != undefined ? options.selectionRequiredMsg : 'Please select at least one item';
	var markerNotFoundMsg = options.markerNotFoundMsg != undefined ? options.markerNotFoundMsg : 'We couldn\'t find the selected location on the map.';
	var addToSelectionConfirmationMsg = options.addToSelectionConfirmationMsg != undefined ? options.addToSelectionConfirmationMsg : 'Would you like to add the selected locations to your selection list?';
	
	var lastQueryTime = 0;
	var mapLoaded = false;
	var map;
	var geocoder;
	var lastSearchResult = null;
	var geocodeItemIndex = 0;
	var markers = []; 
	var selectedItems = [];
	var locations = [];
	var infoWindow; 
	var bounds;
	var uniqeID = (new Date()).getTime();
			
	var DIALOG_ID = 'GoogleMapsLocationSelectorDialog' + uniqeID;
	var ITEM_LIST_ID = 'GoogleMapsLocationSelectorItemList' + uniqeID;
	var SEARCH_BUTTON_ID = 'GoogleMapsLocationSelectorSearchButton' + uniqeID;
	var LOAD_PROGRESS_ID = 'GoogleMapsLocationSelectorLoadProgress' + uniqeID;
	var SIDE_BAR_ID = 'GoogleMapsLocationSelectorSideBar' + uniqeID;
	var QUERY_INPUT_ID = 'GoogleMapsLocationSelectorQueryInput' + uniqeID;
	var MAP_ID = 'GoogleMapsLocationSelectorMap' + uniqeID;
	var ADD_LINK_ID = 'GoogleMapsLocationSelectorAdd' + uniqeID;
	
	var DIALOG_CODE = '<div class="GoogleMapsLocationSelectorDialog" id="' + DIALOG_ID + '" >' +
		  	'<div class="SearchArea">' +
		    	'<table>' +
		    		'<tr>' +
		    			'<td valign="middle">' + searchFieldText + ':</td>' +
		    			'<td><input type="text" id="' + QUERY_INPUT_ID + '" class="TxtBox"/></td>' +
		    			'<td valign="middle">' +
		    				'<input type="button" id="' + SEARCH_BUTTON_ID + '" value="' + searchButtonText + '"/>' +
		    			'</td>' +
		    			'<td valign="middle"><div id="' + LOAD_PROGRESS_ID + '" class="Loading"></div></td>' +
		    		'</tr>' +
		    	'</table>' +
		   		'<div id="Clear"></div>' +
		    '</div>' +
			'<div class="ContentArea">' +
			  '<table>' + 
			    '<tbody>' + 
			      '<tr>' +
			        '<td width="200" valign="top"> <div class="GoogleMapsLocationSelectorSideBar" id="' + SIDE_BAR_ID + '"></div>' +
			        '</td>' +
			        '<td><div class="GoogleMapsLocationSelectorMap" id="' + MAP_ID + '"></div></td>' +
			      '</tr>' + 
			    '</tbody>' +
			  '</table>' +
			'</div>' +
			'<h3>' + selectedListText + '</h3>' +
	   		'<div id="' + ITEM_LIST_ID + '"></div>' +
		'</div>';
	
	this.CALLBACK_OK = 1;
	this.CALLBACK_CANCEL = 0;
	this.CALLBACK_NOT_FOUND = 2;
	
	this.show = function(currentSelection)
	{
		if (!mapLoaded)
		{
			mapLoaded = true;
			jQuery("body").before(DIALOG_CODE);
			initMap();
			jQuery("#" + SEARCH_BUTTON_ID).click(searchItems);
			jQuery("#" + SEARCH_BUTTON_ID).button();
			jQuery("#" + QUERY_INPUT_ID).keypress(function(event) {
				if (event.keyCode == '13') {
					event.preventDefault();
					searchItems();
				}
			});
			window['googleMapLocationSelectorAdd' + uniqeID] = addItem;
			window['googleMapLocationSelectorRemove' + uniqeID] = removeItem;
		}
		jQuery('#' + QUERY_INPUT_ID).val('');
		selectedItems.length = 0;
		if (currentSelection)
		{
			for (var i = 0; i < currentSelection.length; i++)
			{
				selectedItems.push(currentSelection[i]);
			}
		}
		refreshItemList();
		var buttons = {};
		if (notFoundButtonText != '') buttons[notFoundButtonText] = onDialogNotFoundClick;
		buttons[okButtonText] = onDialogOKClick;
		buttons[closeButtonText] = onDialogCloseClick;
		jQuery("#" + DIALOG_ID).dialog({
			width: 630,
			modal: true,
			resizable: false,
			bgiframe: true,
			title: dialogTilteText,
			buttons: buttons
		});
		jQuery('#' + QUERY_INPUT_ID).val(initialQuery);
		jQuery('#' + QUERY_INPUT_ID).focus();
		searchItems();
		this.refreshMap();
	};
	
	this.getSelection = function() 
	{
		var response = new Array();
		for (var i = 0; i < selectedItems.length; i++)
		{
			response.push(selectedItems[i]);
		}
		return response;
	};
	
	this.refreshMap = function()
	{
		google.maps.event.trigger(map, 'resize');
	};
	
	function onDialogOKClick()
	{
		if (required && selectedItems.length == 0)
		{
			alert(selectionRequiredMsg);
			return;
		}
		if (callback) callback.call(window, 1);
		$(this).dialog("close");
	}	
	
	function onDialogNotFoundClick()
	{
		if (callback) callback.call(window, 2);
		$(this).dialog("close");
	}
	
	function onDialogCloseClick()
	{
		if (callback) callback.call(window, 0);
		$(this).dialog("close");
	}

    function initMap()
    {
        var latlng = new google.maps.LatLng(centerLatitude, centerLongitude);
        var myOptions = {
        	zoom: initialZoom,
        	center: latlng,
        	mapTypeId: google.maps.MapTypeId.ROADMAP
        };
        infoWindow = new google.maps.InfoWindow(); 
        geocoder = new google.maps.Geocoder();
        map = new google.maps.Map(document.getElementById(MAP_ID), myOptions);
    }
	
    function searchItems()
    {
    	var query = jQuery('#' + QUERY_INPUT_ID).val();
    	jQuery("#" + LOAD_PROGRESS_ID).show();
    	jQuery('#' + SEARCH_BUTTON_ID).button('disable');

    	url = searchURL.replace("{QUERY}", encodeURIComponent(query));
    	url = url.replace("{LANGUAGE}", language);
    	jQuery.ajax({
			type: "GET",
			url: url,
			dataType: 'json',
			success: function(data, msg)
			{
				if (data)
				{
			         var searchResults = data;
			         clearLocations();

			         bounds = new google.maps.LatLngBounds();
			         jQuery("#" + SIDE_BAR_ID).empty();
			         if (searchResults.length == 0)
			         {
				         jQuery("#" + SIDE_BAR_ID).text(noResultsText);
				    	 jQuery("#" + LOAD_PROGRESS_ID).hide();
					     jQuery('#' + SEARCH_BUTTON_ID).button('enable');
			        	 map.setOptions(getMapOptions(5, new google.maps.LatLng(centerLatitude, centerLongitude)));
			        	 return;
			         }

			         map.setOptions(getMapOptions(5, new google.maps.LatLng(centerLatitude, centerLongitude)));
			         lastSearchResult = searchResults;
			         geocodeItemIndex = 0;
			         loadNextMarker();
				}
			}
    	});
    }
    
    function clearLocations()
    { 
    	infoWindow.close(); 
    	for (var i = 0; i < markers.length; i++)
    	{ 
    		markers[i].setMap(null); 
    	} 
    	markers.length = 0; 
    } 
		   
    function getMapOptions(zoom, center)
    {
    	return {
    		zoom: zoom,
    		center: center
    	};
    }
    
	function loadNextMarker()
	{
		if (geocodeItemIndex < lastSearchResult.length)
		{
			var address = lastSearchResult[geocodeItemIndex].address;
			var lat = lastSearchResult[geocodeItemIndex].lat;
			var lng = lastSearchResult[geocodeItemIndex].lng;
			var sidebar = jQuery('#' + SIDE_BAR_ID);
			var name = lastSearchResult[geocodeItemIndex].name;
			var id = lastSearchResult[geocodeItemIndex].id;
			if (lat == '' || lng == '')
			{
				geocoder.geocode({'address': address}, function(results, status)
				{
					if (status == google.maps.GeocoderStatus.OK) 
					{
						var point = results[0].geometry.location;
						var marker = createMarker(point, id, name, address);
						locations.push({id: id, lat: point.lat(), lng: point.lng()});
						var sidebarEntry = createSidebarEntry(marker, id, name, address);
						sidebar.append(jQuery(sidebarEntry));
						bounds.extend(point);
						geocodeItemIndex = geocodeItemIndex + 1;
						setTimeout(loadNextMarker, geocodeQueryDelay);
					}
					else
					{
						var sidebarEntry = createSidebarEntry(null, id, name, address);
						sidebar.append(jQuery(sidebarEntry));
						geocodeItemIndex = geocodeItemIndex + 1;
						setTimeout(loadNextMarker, geocodeQueryDelay);
					}
				});
			}
			else
			{
				var point = new google.maps.LatLng(lat, lng);
				var marker = createMarker(point, id, name, address);
				var sidebarEntry = createSidebarEntry(marker, id, name, address);
				sidebar.append(jQuery(sidebarEntry));
				bounds.extend(point);
				geocodeItemIndex = geocodeItemIndex + 1;
				setTimeout(loadNextMarker, 1);
			}
		}
		else
		{
		     map.fitBounds(bounds); 
		     if (locations.length > 0) updateLocations();
		     else
		     {
		    	 jQuery("#" + LOAD_PROGRESS_ID).hide();
			     jQuery('#' + SEARCH_BUTTON_ID).button('enable');
		     }
		}
	}
	
	function updateLocations()
	{
		if (updateURL != '')
		{
			jQuery.ajax({
				type: "POST",
				url: updateURL,
				dataType: 'json',
				data: {data: locations},
				success: function(data, msg)
				{
					locations = [];
					jQuery("#" + LOAD_PROGRESS_ID).hide();
					jQuery('#' + SEARCH_BUTTON_ID).button('enable');
				}
			});
		}
		else
		{
			locations = [];
			jQuery("#" + LOAD_PROGRESS_ID).hide();
			jQuery('#' + SEARCH_BUTTON_ID).button('enable');
		}
	}

    function createMarker(point, id, name, address)
    {
        var html = '<div class="GoogleMapsLocationSelectorInfoWindowContent"><b>' + name + '</b> <br/>' + address + '<br/><a class="AddLink" href="javaScript:void(0);" onclick="googleMapLocationSelectorAdd' + uniqeID + '(' + id + ')">' + addToListText + '</a></div>';
        var marker = new google.maps.Marker({ 
          map: map, 
          position: point
        }); 
        google.maps.event.addListener(marker, 'click', function() { 
          infoWindow.setContent(html); 
          infoWindow.open(map, marker); 
        }); 
        markers.push(marker);
        return marker;
    }

    function createSidebarEntry(marker, id, name, address)
    {
      var element = document.createElement('a');
      var html = '<b>' + name + '</b><br/>' + address;
      element.innerHTML = html;
      //div.style.cursor = 'pointer';
      //div.style.marginBottom = '5px'; 
      //div.style.display = 'block'; 
      element.className = "SideBarEntry";
	  if (marker)
	  {
		  jQuery(element).click(function() {
			  google.maps.event.trigger(marker, 'click');
		  });
	  }
	  else
	  {
		  jQuery(element).click(function() {
			if (confirm(markerNotFoundMsg + ' ' + addToSelectionConfirmationMsg))
			{
				addItem(id);
			}
		  });
	  }
      /*jQuery(div).mouseover(function() {
        div.style.backgroundColor = '#eee';
      });
      jQuery(div).mouseout(function() {
        div.style.backgroundColor = '#fff';
      });*/
      return element;
    }

	function refreshItemList()
	{
		var selectList = jQuery('#' + ITEM_LIST_ID);
		selectList.empty();
		for (var i = 0; i < selectedItems.length; i++)
		{
			selectList.append('<div class="Item"><a class="Delete" title="' + deleteButtonText + '" href="javascript:void(0)" onclick="googleMapLocationSelectorRemove' + uniqeID + '(' + selectedItems[i].id + ')"></a><div id="Name">' + selectedItems[i].name + '</div></div>');
		}
		if (selectedItems.length == 0) selectList.text(selectedListEmptyText);
	}

	function removeItem(id)
	{
		for (var i = 0; i < selectedItems.length; i++)
		{
			if (selectedItems[i].id == id) selectedItems.splice(i,1);
		}
		refreshItemList();
	}

	function addItem(id)
	{
		if (singleSelection)
		{
			for (var i = 0; i < lastSearchResult.length; i++)
			{
				if (lastSearchResult[i].id == id)
				{
					selectedItems.length = 0;
					selectedItems.push(lastSearchResult[i]);
					refreshItemList();
					break;
				}
			}
		}
		else
		{
			if (selectedItems.length >= selectionLimit)
			{
				alert(limitExceedMsg);
				return;
			}
			for (var i = 0; i < selectedItems.length; i++)
			{
				if (selectedItems[i].id == id)
				{
					alert(itemAlreadyAddedMsg);
					return;
				}
			}
			for (var i = 0; i < lastSearchResult.length; i++)
			{
				if (lastSearchResult[i].id == id)
				{
					selectedItems.push(lastSearchResult[i]);
					refreshItemList();
					break;
				}
			}
		}
	}
    
}
