
	var ClassEventsMap = Class.create({
		
		mapDiv: 	null,		// holds div#map
		map: 		null,		// the Gmap2 instance
		events: 	[],			// holds all displayed events
		
		center: 	null,		// if umkreissuche, geodaten
		umkreis: 	null,		// if umkreissuche, umkreis in km
		circle:		null,		// Overlay, if exists
		
		view:		null,		// die View klasse, um den Form-Submit individuell zu handeln
		
		viewDetails: null,		// der js View wo die details gezeigt werden, div#-events-latest
		
		results: 	null,		// holds div#events-results div.wrap (first-child)
		
		activePopupId: null,	// TODO: dirty=
		
		populateWithDistances: false,	// whether last population had distances, if yes, regards that on printing results
		
		messages: [], // messages from json search
		
		initialize: function() {
			// TODO: OLD
			//Event.observe($("gesamtUebersicht"), 'click', this.showWholeMap.bind(this));
			
			this.results = $$('div#events-results div.wrap')[0];
			
			this.viewDetails = $('-events-latest');

			if (!GBrowserIsCompatible()) return false;
			// create map
			this.map = new GMap2(document.getElementById("events-map"));
			
			// add controlls
			this.map.addControl(new GLargeMapControl());
			this.map.addControl(new GMapTypeControl());
			
			this.showWholeMap();
			
			/**
			 * init view
			 */
			this.viewFormSubmit = this.viewFormSubmit.bindAsEventListener(this);
			this.initView();
		},
		showWholeMap: function() {
			if (this.center) {
				// Standort
				this.map.setCenter(new GLatLng(Number(this.center.geo_b), Number(this.center.geo_l)), 6);
			}
			else {
				// Kassel ;) Mitte Deutschland
				this.map.setCenter(new GLatLng(51.2976283041019, 9.52410537955017), 5);				
			}
			return false;
		},
		/**
		 * initView
		 * get View class, and change form submission behaviour
		 */
		initView: function() {
			this.view = xPage.getViewById('-events-search');
			this.view.ajaxDisable();
			$$('#' + this.view.id + ' form').each(function(form) {
				Event.observe(form, 'submit', this.viewFormSubmit);
			}.bind(this));
		},
		viewFormSubmit: function(evt) {
			if( evt.currentTarget )
				form = evt.currentTarget;
			else form = evt.target;
			evt.stop();
						
			this.view.setLoading();
			new Ajax.Request(form.action + '/' + Math.random(), {
				method: 'post',
				parameters: form.serialize(),
				onSuccess: function(transport) {
					this.view.unsetLoading();
					var json = transport.responseText.evalJSON();
					if (json) {
						
						// console.log('params', json.params);
						// console.log('data', json.data);
						if (IE.version > 0) {
							correctPNG();
						}
						this.messages = json.messages;
						this.populate(json.events, json.messages);
						this.setUmkreis(json.position, json.umkreis);
						
					}else {
						// console.log("#123 error parsing json data");
					}
				}.bind(this),
				evalScripts: true
			});
		}
		,setUmkreis: function(position, umkreis) {
			 /**
			 * remove umkreis
			 */
			this.removeUmkreis();
			
			this.position = position;
			this.umkreis = umkreis;
			
			if (this.position && this.umkreis) {
				this.map.setCenter(new GLatLng(Number(this.position.geo_b), Number(this.position.geo_l)), 6);
				this.addUmkreis();
			}
		}
		,removeUmkreis: function() {
			if (this.circle) {
				this.map.removeOverlay(this.circle);
				this.circle = null;
			}
		}
		,addUmkreis: function() {
			/**
			 * draw circle
			 */
			 
			var pos = new GLatLng(Number(this.position.geo_b), Number(this.position.geo_l));
			this.circle = getCircle(pos, this.umkreis, 50 /* segments */);
			this.map.addOverlay(this.circle);

			/**
			 * embeded func
			 */
			function getCircle(center, radius, nodes, liColor, liWidth, liOpa, fillColor, fillOpa) {
				//calculating km/degree
				var latConv = center.distanceFrom(new GLatLng(center.lat()+0.1, center.lng()))/100;
				var lngConv = center.distanceFrom(new GLatLng(center.lat(), center.lng()+0.1))/100;
			
				//Loop 
				var points = [];
				var step = parseInt(360/nodes)||10;
				for(var i=0; i<=360; i+=step)
				{
					var pint = new GLatLng(center.lat() + (radius/latConv * Math.cos(i * Math.PI/180)), center.lng() + (radius/lngConv * Math.sin(i * Math.PI/180)));
					points.push(pint);
					//bounds.extend(pint); //this is for fit function
				}
				points.push(points[0]); // Closes the circle, thanks Martin
				fillColor = fillColor||liColor||"#0055ff";
				liWidth = liWidth||2;
				var poly = new GPolygon(points,liColor,liWidth,liOpa,fillColor,fillOpa);
				return poly;
			}
		}
		,eventById: function(id) {
			for (var index = 0; index < this.events.length; index++) {
				if (this.events[index].Event.id == id) break;
			}
			return this.events[index];
		}
		,eventPopup: function(event_id) {
			
			if (this.activePopupId == event_id) {
				return false;
			}
			this.activePopupId = event_id;
			
			var event = this.eventById(event_id);
			
			var html = '';
			html += '<table><tr><td valign="top" width="76">';
			html += '<img src="' + event.Event.image + '" width="66" height="53" style="border: 1px solid #9a9a9a;" />';
			html += '</td><td valign="top">';
			html += event.Event.date_nice + '<br />';
			html += event.Event.location + '<br />';
			html += event.EventsType.name + '<br />';
			html += '</td></tr></table>';
			
			var text = new Element('div', {}).update(html);
			
			var a = new Element('a', {
				'rel': event.Event.id,
				'href': '#'
			}).update("Zeige Party");
			text.appendChild(a);

			event.marker.openInfoWindow(text);
			
			var clickHandler = this.onEventClick.bind(this);
			Event.observe(a, 'click', clickHandler);
		}
		,populate: function(eventsData) {
			
			/**
			 * {
			 * 	Event: {
			 * 		id: int
			 * 		,geo_l: float
			 * 		,geo_b: float
			 * 		,..
			 * 	}
			 * 	,Images: [
			 * 		
			 * 	]
			 * }
			 */
			
			this.populateWithDistances = false;
			
			var ref = this;
			
			var newDistances = [];
			
			var idsNew = [];
			var idsOld = [];
			
			eventsData.each(function(n) {
				if (n.Event.id) {
					idsNew.push(n.Event.id);
					
					// save distance to temporary assoziative array
					// to update staying items later on
					if (n.Event.distance != null) {
						newDistances['' + n.Event.id] = n.Event.distance;
					} 
				}
				
				// if it was a search, regading the distance 
				if (n.Event.distance != null) {
					ref.populateWithDistances = true;
				}
			});
			
			this.events.each(function(n) {
				if (n.Event.id)
					idsOld.push(n.Event.id);
			});
			
//			console.log("new Ids", idsNew);
//			console.log("old Ids", idsOld);
			
			var idsAppend = [];
			var idsRemove = [];
			var idsStay = [];
			
			idsNew.each(function(n) {
				if (idsOld.indexOf(n) > -1) {
					// is already there
				}
				else {
					// is new here
					idsAppend.push(n);
				}
			});
			
			idsOld.each(function(n) {
				if (idsNew.indexOf(n) > -1) {
					// is still there
					idsStay.push(n);
				}
				else {
					// us no longer there
					idsRemove.push(n);
				}
			});
			
//			console.log("append Ids", idsAppend);
//			console.log("remove Ids", idsRemove);
			
			/**
			 * remove items
			 */
			var remObjs = []; 
			idsRemove.each(function(idRemove) {
				for (var index = 0; index < this.events.length; index++) {
					if (this.events[index].Event.id == idRemove) {
						this.removeEvent(this.events[index]);
						remObjs.push(this.events[index]);
					}
				}
			}.bind(this));
			
			/**
			 * acutally remove items from this.events
			 * doin it here and not in this.removeEvent because remObjs collects items,
			 * to increase the speed of Array.without()
			 */
			this.events = this.events.without.apply(this.events, remObjs);
			
			/**
			 * add items
			 */
			idsAppend.each(function(idAppend) {
				for (var index = 0; index < eventsData.length; index++) {
					if (eventsData[index].Event.id == idAppend) {
						this.addEvent(eventsData[index]);
					}
				}
			}.bind(this));
			
			/**
			 * bissel krum, zugegebenermassen
			 * die distance wird nachgebaut, da ich das originale object da lasse
			 * und aus eventsData nur dir neuen kopiere
			 * 
			 * TODO: neuer Ansatz wäre besser ... this.events überschreiben
			 * aber .marker beibehalten
			 */
			idsStay.each(function(idStay) {
				for (var index = 0; index < this.events.length; index++) {
					if (this.events[index].Event.id == idStay) {
						
						if (newDistances['' + idStay]) {
							this.events[index].Event.distance = newDistances['' + idStay]; 
						}
					}
				}
			}.bind(this)); 
			
			
			/**
			 * set results content
			 */
			this.updateResults();
		}
		,updateResults: function() {

			// Event handlers			
			var clickHandler = this.onEventClick.bind(this);
			var mouseoverHandler = this.onEventMouseOver.bind(this);
			
			// remove old Event listeners
			$$('div#events-results div.event a').each(function(link) {
				Event.stopObserving(link, 'click', clickHandler);
			});
			$$('div#events-results div.event a').each(function(link) {
				Event.stopObserving(link, 'mouseover', mouseoverHandler);
			});
			
			/**
			 * order events by date
			 */
			this.events.sort(function(a,b) {
				
				a = a.Event.date;
				b = b.Event.date;
				
				// if same date
				if (a == b) return 0;
				
				tmp = [a,b].sort();
				
				if (tmp[0] == a)
					return 1;
				else
					return -1;
			});
			
			/**
			 * set new contents
			 */
			var html = '';
			
			if (this.events.length < 1 && this.messages.length < 1) {
				this.messages.push("Es wurden keine Parties gefunden");
			}
			
			if (this.messages.length > 0) {
				html += '<div class="messages"><span class="message">' + this.messages.join('</span><br />') + '</div>';
			}
		
			for (var index = 0; index < this.events.length; index++) {
				html += this.createResultHtml(this.events[index]);
			}
			html += '<div class="bottomspace"></div>';
			this.results.update(html);
			
			// add Event listeners
			$$('div#events-results div.event a').each(function(link) {
				Event.observe(link, 'click', clickHandler);
			});
			$$('div#events-results div.event a').each(function(link) {
				Event.observe(link, 'mouseover', mouseoverHandler);
			});
			
		}
		,onEventClick: function(evt) {
			evt.stop();
			if( evt.currentTarget )
				a = evt.currentTarget;
			else a = evt.target;
			if (IE.version > 0 && a.tagName.toLowerCase() != "a") {
				a = a.parentNode;
			}
			
			// show details, by event id
			this.eventDetails(a.rel);
			return false;
		}
		,onEventMouseOver: function(evt) {
			evt.stop();
			if( evt.currentTarget )
				a = evt.currentTarget;
			else a = evt.target;
			if (IE.version > 0 && a.tagName.toLowerCase() != "a") {
				a = a.parentNode;
			}
			
			// show details, by event id
			this.eventPopup(a.rel);
			return false;
		}
		/**
		 * 
		 */
		,eventDetails: function(event_id) {
			var event = this.eventById(event_id);
			
			var ViewClass = xPage.getViewById('-events-latest');
			
			ViewClass.load('/events/details/' + event.Event.id);
			
//			new Ajax.Updater(this.viewDetails, '/events/details/' + event.Event.id, {
//  				parameters: {
//  				}
//			});
		}
		,createResultHtml: function(event) {
			
			var $link			= '<a href="#" class="eventlink" rel="' + event.Event.id + '">';
			var $link_details	= '<a href="#" class="eventlink details" rel="' + event.Event.id + '">';
			var $link_image		= '<a href="#" class="eventlink imglink" rel="' + event.Event.id + '">';
			
			var $date 		= event.Event.date_nice;
			var $location	= event.Event.location;
			var $type		= event.EventsType.name;
			var $description	= event.Event.description.substr(0, 65) + '...';
			var $image 		= event.Event.image;
			
			var $distance;
			//if (event.Event.distance != null) { // old
			if (this.populateWithDistances) {
				$distance = ' | ' + event.Event.distance + ' km Entfernung';
			}
			else {
				$distance = '';
			}
			
			var html = '<div class="event">' +
			'	<div class="space">' + 
			'		' + $link_image + '<img src="' + $image + '" width="67" height="56" /></a>' + 
			'		<strong>' + $link + $date + $distance + '</a></strong><br />' + 
			'		' + $link + $location + ' (' + $type + ')</a><br />' + 
			'			' + $link_details + 'Details</a>' + 
			'		' + $description + 
			'	</div>' + 
			'</div>';
			return html;
			
		}
		,addEvent: function(event) {
			this.events.push(event);
			this.createMarker(event);
		}
		,removeEvent: function(event) {			
			this.map.removeOverlay(event.marker);
			event.marker = null;
		}
		,createMarker: function(event) {
			
			var markerOptions = {
				//icon: icon
			};
			var point = new GLatLng(event.Event.geo_b, event.Event.geo_l);
			var marker = new GMarker(point, markerOptions);
			
			marker.id = event.Event.id;
			marker.foo = "bar";
			event.marker = marker;
			
			var app = this;
			GEvent.addListener(marker, "click", function() {
				app.eventPopup(this.id);
			});
			
			this.map.addOverlay(marker);
		}
	});
	
	var EventsMap;
	var eventsData;
	var position;
	var umkreis;
	
	function eventsInit() {
		EventsMap = new ClassEventsMap();
		EventsMap.setUmkreis(position, umkreis);
		EventsMap.populate(eventsData);
		
		/**
		 * make search datum hybsch wa?
		 * js/test/jquery.js
		 * js/test/jquery.noconflict.js
		 * css/datepicker.css
		 */
		jQuery('#EventDateFrom').DatePicker({
			format:		'd.m.Y',
			date:		[],
			mode:		'range',
			starts:		1,
			position:	'bottom',
		    calendars: 	1,
			onChange: function(formated, dates){
				$('EventDateFrom').value 	= formated[0];
				$('EventDateTo').value 		= formated[1];
			},
			onBeforeShow: function(){
				var dates = [
					$F('EventDateFrom'),
					$F('EventDateTo')
				];
				jQuery('#EventDateFrom').DatePickerSetDate(dates, true);
			}
		});
	
		// to let second field pop up calendar
		Event.observe($('EventDateTo'), 'click', function() {
			jQuery('#EventDateFrom').DatePickerSecondSelection();
			jQuery('#EventDateFrom').DatePickerShow();
		});
		
	}
	
	Event.observe(window, 'load', eventsInit);
	Event.observe(window, 'unload', GUnload);		// unloads google maps, see their API
	