

/*
	WorldApp
*/
var WorldApp = Class.extend({}, {
	
	initialize: function(options) {
		this.map = null;

		this.options = this.initialize_state(options);

		if (window.addEventListener)
			window.addEventListener("unload", this.serializeState, false);
		else if (window.attachEvent)
			window.attachEvent("onbeforeunload", this.serializeState);

	},
	
	initialize_state: function(options) {		
		app.use('tools.gmap.kmap');
		this.map = new geobirds_gmap($(options.map_element));	
				
		if (options._start_data_id) {
			this.map.setCenter(new GLatLng(options._start_data_lat, options._start_data_lng),
						options.start_zoom, G_NORMAL_MAP);	
		} else {
			options = this.initialize_map_position(options);
			this.map.setCenter(new GLatLng(options.start_lat, options.start_lng),
						options.start_zoom, G_NORMAL_MAP);	
		}
		
		this.initialize_add_selectors();
		options.fromdate = this.initialize_date_selector(options);
		return options;
	},
	
	initialize_map_position: function(options) {
		if ( (options.start_lat == -999) || (options.start_lng == -999) ) {	
			app.use('lib.cookies');
			options.start_lat = getCookie("idlatitude");
			options.start_lng = getCookie("idlongitude");

			if ( (options.start_lng == 'null') || (options.start_lng == null) ) { 
				options.start_lng = GMap2.BASE_LONGITUDE; 
				options.start_zoom = GMap2.COUNTRY_ZOOM_LEVEL; 
			}
			if ( (options.start_lat == 'null') || (options.start_lat == null) ) { 
				options.start_lat = GMap2.BASE_LATITUDE;  
				options.start_zoom = GMap2.COUNTRY_ZOOM_LEVEL; 
			}
		}
		return options;
	},
	
	initialize_date_selector: function(options) {
		var app = this;
		
		if (options._start_data_date) {
			var start_date = Utils.mysqldate2js(options._start_data_date);

			if ( start_date.getTime() > ( (new Date()).getTime() - (1000*60*60*24) ) ) $('dateselect').value = '24hrs';
			else if ( start_date.getTime() > ( (new Date()).getTime() - (1000*60*60*24*7) ) ) $('dateselect').value = '7days';
			else if ( start_date.getTime() > ( (new Date()).getTime() - (1000*60*60*24*30) ) ) $('dateselect').value = '30days';
			else if ( start_date.getTime() > ( (new Date()).getTime() - (1000*60*60*24*90) ) ) $('dateselect').value = '90days';
			else if ( start_date.getTime() > ( (new Date()).getTime() - (1000*60*60*24*180) ) ) $('dateselect').value = '180days';
			else $('dateselect').value = '365days';
		}			
		
		$('dateselect').onchange = function() {
			if (this.value.match(/24hrs/)) {
				newdate = new Date((new Date()).getTime() - (1000*60*60*24));
			} else if (this.value.match(/(\d+)days/)) {
				newdate = new Date((new Date()).getTime() - (1000*60*60*24*Number(RegExp.$1)));
			}	
			if (app.models) {
				app.models.each(function(m) { m.setFromDate(newdate, app.map.getZoom(), app.map); });
			}
			return newdate;
		}
		
		return ($('dateselect').onchange());
	},
	
	initialize_add_selectors: function(options) {
		var app = this;

		if ($('addsighting')) $('addsighting').onclick = function() {
						app.triggerAdd(app.sightingmapview);
					}
		
		if ($('addhotspot')) $('addhotspot').onclick = function() {
						app.triggerAdd(app.hotspotmapview);	
					}

		if ($('addclub')) $('addclub').onclick = function() {
						app.triggerAdd(app.clubmapview);		
					}		
	},
	
	triggerAdd: function(view) {
		if (user == '') {
			app.use('ui.dialog');
			GeobirdsWarningAlert('Contributions require membership in the Geobirds community!<br />Please sign up and/or login, then try this again.',
						'Please log in',-1,-1,'OK');			
		} else {
			this.mapviews.each(function(node) {
				node.controller.cancelAdd();
			});
			view.controller.prepareAdd();
		}
	},
	
	switchTo: function(model) {
		var ne = model;
		this.models.each(function(m) {
			if (m.typeid == model) {
				this.enabledModel = m;
				m.enabled = true;
				Element.show($(m.typeid + 'list'));
				m.notify(new GeoEvent(GeoEvent.ENABLED, {}));
				m.notify(new GeoEvent(GeoEvent.SELECT_ALL, {}));
			} else {
				if (m.enabled) ne = m.typeid;
				m.enabled = false;
				Element.hide($(m.typeid + 'list'));
				m.notify(new GeoEvent(GeoEvent.SELECT_NONE, {}));
			}
		}.bind(this));
		
		if ($('postlink')) {
			if (model == 'sighting') {
				$('postlink').innerHTML = "<a href='#' id='addsighting'>Post a sighting</a>";
			} else if (model == 'hotspot') {
				$('postlink').innerHTML = "<a href='#' id='addhotspot'>Post a birding hotspot</a>";
			} else if (model == 'club') {
				$('postlink').innerHTML = "<a href='#' id='addclub'>Post a birding club</a>";
			} else if (model == 'user') {

			}
		}
		
		if (model == 'sighting') Element.show('daterange', 'sightingsort'); else Element.hide('daterange', 'sightingsort');
		if (model == 'user') model = 'user|member';
		
		if ($('explorelink')) {
			$('explorelink').innerHTML = $('explorelink').innerHTML.replace(new RegExp(model, "gi"), ne).replace(/users/, "members");
		}
		
		this.initialize_add_selectors();
	},
	
	initialize_models: function() {
		var options = this.options;
		
		app.use('app.logic.world.sighting');
		this.sightingmodel = new SightingCollectionModel(options);		
		if ((options.data != 'all') && (options.data != 'sightings')) this.sightingmodel.enabled = false;
		this.sightinglistview = new SightingListView(this.sightingmodel, ListController);
		this.sightingmapview = new SightingMapView(this.sightingmodel,this.map);

		app.use('app.logic.world.hotspot');		
		this.hotspotmodel = new HotspotCollectionModel(options);
		if ((options.data != 'all') && (options.data != 'hotspots')) this.hotspotmodel.enabled = false;
		this.hotspotlistview = new HotspotListView(this.hotspotmodel, ListController);
		this.hotspotmapview = new MapView(this.hotspotmodel,this.map);

		app.use('app.logic.world.user');
		this.usermodel = new UserCollectionModel(options);
		if ((options.data != 'all') && (options.data != 'users')) this.usermodel.enabled = false;
		this.userlistview = new UserListView(this.usermodel, ListController);
		this.usermapview = new MapView(this.usermodel, this.map);
		
		app.use('app.logic.world.club');
		this.clubmodel = new ClubCollectionModel(options);
		if ((options.data != 'all') && (options.data != 'clubs')) this.clubmodel.enabled = false;
		this.clublistview = new ClubListView(this.clubmodel, ListController);
		this.clubmapview = new MapView(this.clubmodel, this.map);

		this.models = [this.sightingmodel, this.hotspotmodel, this.usermodel, this.clubmodel];	
		this.mapviews = [this.sightingmapview, this.hotspotmapview, this.usermapview, this.clubmapview];

		this.models.each(function(m) {
			if (m.enabled) this.enabledModel = m;
		}.bind(this));
		
		this._loading_semaphore = 0;
		var theapp = this;
		this.models.each(function(node) { node.attach(theapp); });

		this.map.setMapType(G_SATELLITE_MAP);		
		this.map.setMapType(G_NORMAL_MAP);
		
		this.resumeState();
	},
	
	update: function(event) {
		if (event.type == GeoEvent.START_LOAD) {
			this._loading_semaphore++;
			$('loading_indicator').style.display = 'block';
		} else if (event.type == GeoEvent.END_LOAD) {
			// hide it later
			this._loading_semaphore--;
			if (this._loading_semaphore <= 0) {
				this._loading_semaphore = 0;
				Element.hide('loading_indicator');
			}
		}
	},
	
	serializeState: function() {
		var obj = {};
		obj.zoom = worldapp.map.getZoom();
		obj.center = {};
		obj.center.lat = worldapp.map.getCenter().lat();
		obj.center.lng = worldapp.map.getCenter().lng();
		obj.maptype = worldapp.map.getCurrentMapType().getUrlArg();
		obj.dateselect = $('dateselect').value;
		obj.tab = {};
		
		obj.models = [];
		worldapp.models.each(function(m,i) {
			if (m.enabled) obj.models.push(m.typeid);
		});

		obj.sightingsort = worldapp.sightinglistview.sort;

		if (Element.visible('map')) {
			obj.mode = 'exploring';
		} else {
			var scr = ['editsightingscreen','commentsightingscreen','addsightingscreen',
			'edithotspotscreen','commenthotspotscreen','addhotspotscreen',
			'editclubscreen','commentclubscreen','addclubscreen'].detect(function(m) {
				return (Element.visible(m));
			});

			obj.mode = scr;
			
			if (scr.match(/add/)) {
				var selectedData = 0;
				if (scr.match(/sighting/)) selectedData = worldapp.sightingmodel.data;
				else if (scr.match(/hotspot/)) selectedData = worldapp.hotspotmodel.data;
				else if (scr.match(/club/)) selectedData = worldapp.clubmodel.data;			
				
				obj.selectedData = {};
				obj.selectedData.lat = selectedData.lat();
				obj.selectedData.lng = selectedData.lng();
			} else {
			 	var selectedId = 0;
				if (scr.match(/sighting/)) selectedId = worldapp.sightingmodel.selectedObject.id;
				else if (scr.match(/hotspot/)) selectedId = worldapp.hotspotmodel.selectedObject.id;
				else if (scr.match(/club/)) selectedId = worldapp.clubmodel.selectedObject.id;			

				obj.selectedId = selectedId;			
			}
		
		}

		location.hash = toJsonString(obj);
		return obj;

	},
	
	resumeState: function() {
		if (location.hash.substr(1)) {
			var state = eval('(' + location.hash.substr(1).replace(/\\\"/g, '"') + ')');

			if ((state.redirect) && (state.redirect == 'login')
			    && (!this.options._start_data_id) && (state.mode == 'exploring') ) {		
				var conf = confirm('reload previous page state?');
				if (!conf) return;
			}
		
			worldapp.map.setCenter(new GLatLng(state.center.lat,state.center.lng));
			var maptype = worldapp.map.getMapTypes().detect(function(m) {
				return (m.getUrlArg() == state.maptype);
			});
			worldapp.map.setZoom(state.zoom);
			worldapp.map.setMapType(maptype);
		
			state.models.each(function(m,i) {
				var model = worldapp.models.find(function(n) { return (n.typeid == m); });
				model.move(worldapp.map.getBounds(), worldapp.map.getZoom(), worldapp.map);
				worldapp.switchTo(m);
			});			

			$('dateselect').value = state.dateselect;
			
			worldapp.sightinglistview.sort = state.sightingsort;
			$(state.sightingsort).onclick();
			
			if (state.mode == 'exploring') {
				// do nothing
			} else {	
				this.state = state;
				window.setTimeout("worldapp.resumeAct()", 500);
			}
					
		}
	},
	
	resumeAct: function() {
		var state = this.state;
		if (state.mode.match(/sighting/)) {
			if (state.mode.match(/add/)) 
				worldapp.sightingmodel.act(state.mode.replace(/sighting.*$/, ''), 
					new GLatLng(state.selectedData.lat, state.selectedData.lng));
			else {
				worldapp.sightingmodel.selectid(state.selectedId);
				worldapp.sightingmodel.act(state.mode.replace(/sighting.*$/, ''));
			}
		} else if (state.mode.match(/hotspot/)) { 		
			if (state.mode.match(/add/)) 
				worldapp.hotspotmodel.act(state.mode.replace(/hotspot.*$/, ''), 
				new GLatLng(state.selectedData.lat, state.selectedData.lng));
			else {
				worldapp.hotspotmodel.selectid(state.selectedId);
				worldapp.hotspotmodel.act(state.mode.replace(/hotspot.*$/, ''));
			}
		} else if (state.mode.match(/club/)) {
			if (state.mode.match(/add/)) 
				worldapp.clubmodel.act(state.mode.replace(/club.*$/, ''), 
				new GLatLng(state.selectedData.lat, state.selectedData.lng));
			else {
				worldapp.clubmodel.selectid(state.selectedId);
				worldapp.clubmodel.act(state.mode.replace(/club.*$/, ''));
			}
		}	
	}
});


/*
	ListView
*/

var ListView = Class.extend(View, {

	initialize: function(model,ControllerClass) {
		this.model = model;
		this.initialize_state();
		
		this.controller = new ControllerClass(this);
		this.model.attach(this);	


	},

	initialize_state: function() {
		this.sort = null;

	},
	
	setSort: function(newSort) {
		if (newSort != this.sort) {
			this.sort = newSort;
			this.refreshList();
		}
	},
	
	update: function(event) {		
		if ( (event.type == GeoEvent.MOVE) || 
		     (event.type == GeoEvent.REMOVE) ||
		     (event.type == GeoEvent.DATE_CHANGE) || 
		     (event.type == GeoEvent.SELECT_ALL) || 
		     ((event.type == GeoEvent.SELECT_PROPERTY) && (event.data == 'user')) ) {	
			this.refreshList();
			if (this.controller.lastselected) {
				this.doHighlight(this.controller.lastselected, true);			
			}
		} else if ( (event.type == GeoEvent.SELECT) || 
		            (event.type == GeoEvent.MARKER_SELECT) ) {
			if (this.controller.lastselected) {
				this.doHighlight(this.controller.lastselected, false);			
			}
			this.doHighlight(this.prefix + event.data.id,true);
			this.controller.lastselected = this.prefix + event.data.id;
		} else if (event.type == GeoEvent.SELECT_NONE) {
			this.refreshList();
		}
	},
	
	refreshList: function() {
		this.list.innerHTML = '';
		this.refreshCategorySort();
	},
	
	refreshCategorySort: function() {
	
	},
	
	listText: function (category,label,id,trigger) {
		var text = '<div class="birdlist' + category + ' ' + this.selector + '" id="' + id + '"';
		text += ' onmouseover="worldapp.' + this.model.typeid + 'listview.controller.onMouseOver(this);"';
		text += ' onmouseout="worldapp.' + this.model.typeid + 'listview.controller.onMouseOut(this);"';
		text += ' onclick="worldapp.' + this.model.typeid + 'listview.controller.onClick(this);"';
		text += '>' + label + '</div>';
		return text;
	},
	
	highlight: function(elemid,select) {
		if (elemid == this.controller.lastselected) return;		
		this.doHighlight(elemid,select);
	},
	
	doHighlight: function(elemid,select) {
		if (!$(elemid)) return;
		
		if (select) {
			var tmp_color = ($(elemid).style.color) ? $(elemid).style.color : '#75726F';
			$(elemid).style.backgroundColor = tmp_color;
			$(elemid).style.color = '#FFFFFF';		
		} else {
			var tmp_color = ($(elemid).style.backgroundColor) ? $(elemid).style.backgroundColor : '#75726F';
			$(elemid).style.backgroundColor = '#FFFFFF';
			$(elemid).style.color = tmp_color;	
		}
	}

});

/*
	ListController
*/
var ListController = Class.extend(Controller, {

	initialize: function(view) {
		Controller.prototype.initialize.call(this,view);
		this.initialize_state();
	},
	
	initialize_state: function() {
		var cont = this;
		this.rules = {};
		this.lastselected = null;

	},
	
	update: function() {
	},
	
	onClick: function(elem) {
		if ( (this.lastselected) && (this.lastselected != elem.id) ) {
			this.view.doHighlight(this.lastselected,false);
		}
		this.lastselected = elem.id;	
		this.model.selectid(elem.id);	
	},
	
	onMouseOver: function(elem) {
		this.view.highlight(elem.id,true);
	},
	
	onMouseOut: function(elem) {
		this.view.highlight(elem.id,false);
	}
	
});


/*
	MapView
*/
var MapView = Class.extend(View, {
	
	initialize: function(model,map) {
		this.model = model;
		this.map = map;
		
		this.initialize_state();		

		this.controller = new MapController(this);
		this.model.attach(this);	
	},
	
	initialize_state: function() {
		var view = this;
		GEvent.addListener(this.map, "moveend", function() {
			view.model.move(view.map.getBounds(), view.map.getZoom(), view.map);		
		});
		
		this.model.move(this.map.getBounds(), this.map.getZoom(), view.map);
	},
	
	update: function(event) {
		if (event.type == GeoEvent.NEW_DATA) {
			this.showMarkers(event.data);
		} else if (event.type == GeoEvent.MARKER_SELECT){
			this.map.panTo(new GLatLng(event.data.latitude,event.data.longitude));
		} else if (event.type == GeoEvent.SELECT_PROPERTY) {
			this.displayAllMarkers(false);
			this.showMarkers(this.model.selectedObjects);
			if (this.model.selectedObjects.length == 1) this.model.select(this.model.selectedObjects[0]);
		} else if (event.type == GeoEvent.SELECT_ALL) {
			if (event.data == 'overview') {
				this.showOverviewMarkers(this.model.overviewObjects);
			} else {
				this.displayAllMarkers(true);
			}
		} else if (event.type == GeoEvent.SELECT_NONE) {
			if (event.data == 'overview') {
				this.hideOverviewMarkers();			
			} else {
				this.displayAllMarkers(false);						
			}
		} else if (event.type == GeoEvent.SELECT) {
			event.data.marker.doClick();
			this.map.panTo(new GLatLng(event.data.latitude,event.data.longitude));
		} else if (event.type == GeoEvent.SHOW_DETAIL) {
			this.displayAllMarkers(true);
		} else if (event.type == GeoEvent.SHOW_OVERVIEW) {
			this.displayAllMarkers(false);
		} else if (event.type == GeoEvent.DATE_CHANGE) {
			var view = this;
			this.displayAllMarkers(false);
			this.showMarkers(this.model.objectsInDate());
		} else if (event.type == GeoEvent.ZOOMTO) {
			event.data.marker.closeDetailWin();
			this.map.setCenter(new GLatLng(event.data.latitude,event.data.longitude), 14);
			event.data.marker.doClick();
		} else if (event.type == GeoEvent.UPDATE) {
			event.data.marker.closeDetailWin();
			event.data.marker.doClick();		
		} else if (event.type == GeoEvent.REMOVE) {
			event.data.marker.closeDetailWin();
			this.map.removeOverlay(event.data.marker);
		} else if (event.type == GeoEvent.OVERVIEW_DATA) {			
			this.showOverviewMarkers(event.data);
		} else if (event.type == GeoEvent.CLEAR_OVERVIEW) {
			this.hideOverviewMarkers();	
		}
	},
	
	showOverviewMarkers: function(objs) {
		if (objs) {
			this.showMarkers(objs);
		}
	},
	
	hideOverviewMarkers: function() {
		var view = this;
		this.model.overviewObjects.each(function(node) {
			node.marker.display(false);
		});	
	},
	
	displayAllMarkers: function(vis) {
		var objs = $H(this.model.objects).values();
		if (vis) {
			this.showMarkers(objs);
		} else {
			objs.each(function(node) {
				node.marker.display(vis);
			});
		}
	},
	
	showMarkers: function(objs) {
		var view = this;
		objs.each(function(node) {
			view.doShowMarker(node);
		});
	},
	
	doShowMarker: function(obj) {
		if (obj.marker.added) {
			obj.marker.display(true);
		} else {
			this.map.addOverlay(obj.marker);
			obj.marker.added = true;
			obj.attach(this);
			obj.attach(this.controller);	
		}		
	}
});


/*
	MapController
*/
var MapController = Class.extend(Controller, {

	initialize: function(view) {
		Controller.prototype.initialize.call(this,view);
		
		this.rules = null;
		this.lastselected = null;
		this.addMarker = null;
		this.mapListener = null;
		
		this.initialize_state();
	},
	
	initialize_state: function() {
	},
	
	update: function(event) {
		if (event.type == GeoEvent.MARKER_SELECT) {
			if ((this.lastselected) && (this.lastselected != event.data)) this.lastselected.marker.closeDetailWin();
			this.lastselected = event.data;
		} else if (event.type == GeoEvent.SELECT) {
			if ((this.lastselected) && (this.lastselected != event.data)) this.lastselected.marker.closeDetailWin();
			this.lastselected = event.data;		
		} else if ( (event.type == GeoEvent.SELECT_PROPERTY)
		         || (event.type == GeoEvent.SELECT_ALL)
		         || (event.type == GeoEvent.SELECT_NONE) ) {
			if (this.lastselected) this.lastselected.marker.closeDetailWin();
			this.lastselected = null;
		} else if (event.type == GeoEvent.ACTION) {
			document.getElementsByClassName('entryscreen').each(function(node) {
				Element.hide(node);
			});
		} else if (event.type == GeoEvent.ENABLED) {
			this.model.move(this.view.map.getBounds(), this.view.map.getZoom(), this.view.map);
		}
	},
	
	prepareAdd: function() {
		var controller = this;

		this.addMarker = this.createAddMarker(this.view.map.getCenter());
		this.view.map.addOverlay(this.addMarker);

		GEvent.clearListeners(this.addMarker, "click");
		GEvent.addDomListener(this.addMarker, "click",  function() {
			controller.model.act('add',controller.addMarker.getPoint());
			controller.cancelAdd();
		});
		
		$('addhelp').innerHTML = $('addhelp').innerHTML.replace(/item|sighting|hotspot|club/g, this.model.typeid);
		$('cancelAdd').onclick = function() { controller.cancelAdd(); return false; };
		
		Element.show($('addhelp'));
	},

	cancelAdd: function() {
		this.view.map.removeOverlay(this.addMarker);
		Element.hide($('addhelp')); 
	},
	
	createAddMarker: function(point) {
		var marker = new PdMarker(point, {draggable:true});
		marker.setTooltip('click on me to enter details');
		marker.enableDragging();
		return marker;
	}
	
});


/*
	GeoModel
*/
var GeoCollectionModel = Class.extend(CollectionModel, {
	
	initialize: function(options) {
		$C(CollectionModel).initialize.call(this,options);
		this.selectedObject = null;	
		this.lastSelectedObject = null;
		this.selectedPropertyValue = null;
		this.selectedObjects = [];
		this.objects = {};
	},
		
	move: function(bounds, zoom) {
		this.notify(new GeoEvent(GeoEvent.MOVE,bounds));
	},

	select: function(object) {
		this.doSelect(object);
		this.notify(new GeoEvent(GeoEvent.SELECT,object));
	},
	
	selectid: function(id) {
		this.select(this.objects[id]);
	},
	
	selectProperty: function(property,value) {
		this.doSelect(null);
		this.selectedPropertyValue = value;
		
		this.selectedObjects = this.objectsInBounds().findAll(function(node) {
			return (node[property] == value);	
		});
		this.notify(new GeoEvent(GeoEvent.SELECT_PROPERTY, property));
	},

	doSelect: function(object) {
		this.lastSelectedObject = this.selectedObject;
		this.selectedObject = object;	
	},

	objectsInBounds: function() {
		if (this.enabled) {
			var collection = this;
			return $H(this.objects).values().findAll(function(node) {
				return (collection.currentBounds.contains(node.marker.getPoint()));		
			});
		} else {
			return [];
		}	
	}	
});

/*
	GeoDBCollectionModel
*/
var WorldDBCollectionModel = Class.extend(GeoCollectionModel, {

	initialize: function(options) {
		$C(GeoCollectionModel).initialize.call(this,options);
		this.loadedBounds = null;
		this.currentBounds = null;
		this.server = options.db_server;
		this.method = options.db_method;
		this.nextEvent = null;
		this.maxDate = this.fromdate;
		this._object_model = Object;
		this.typeid = '';
		this.loaded = {};
		this.bootstraps = {};
		this.prefix = '';
		this.enabled = true;
	},
	
	setFromDate: function(newdate) {
		this.fromdate = newdate;
		if (this.enabled) {
			if (newdate >= this.maxDate) {	
				this.notify(new GeoEvent(GeoEvent.DATE_CHANGE, {}));
			} else {
				this.maxDate = newdate;
				this.nextEvent = GeoEvent.DATE_CHANGE;
				var tmpbounds = this.loadedBounds;
				this.loadedBounds = null;
				this.move(tmpbounds);		
			}
		}
	},
	
	objectsInDate: function() {
		if (this.enabled) {
			var collection = this;
			return $H(this.objects).values().findAll(function(node) {
				return (node.jsdate >= collection.fromdate);
			});
		} else {
			return [];
		}
	},
	
	move: function(bounds, zoom, map) {

		this.currentBounds = bounds;
		if (!(this.enabled)) {
			this.notify(new GeoEvent(GeoEvent.MOVE,this.currentBounds));	
		} else {
			this.nextEvent = GeoEvent.MOVE;

			if (!this.loadedBounds) {
				// first load
				this.loadedBounds = bounds;
				this.hitDatabase(bounds, map);

			} else if (this.loadedBounds.containsBounds(bounds)) {
				// no load -- just a zoom in
				this.notify(new GeoEvent(GeoEvent.MOVE,this.currentBounds));

			} else if (bounds.containsBounds(this.loadedBounds)) {
				// load -- zoom out
				this.loadedBounds = bounds;
				this.hitDatabase(bounds, map);

			} else if (!this.loadedBounds.intersects(bounds)) {
				// geocoder discrete move -- same as zoom out
				this.loadedBounds = bounds;
				this.hitDatabase(bounds, map);

			} else {
				// pan -- load edges

				var a = this.loadedBounds;
				var b = bounds;
				var bounds1 = new GLatLngBounds();
				var bounds2 = new GLatLngBounds();

				// Find the edges that don't overlap
				if (b.getSouthWest().lat() < a.getSouthWest().lat()) {
					if (b.getSouthWest().lng() < a.getSouthWest().lng()) {
						// south west		
						bounds1 = new GLatLngBounds( new GLatLng(b.getSouthWest().lat(),b.getSouthWest().lng()), new GLatLng(a.getNorthEast().lat(),a.getSouthWest().lng()) );
						bounds2 = new GLatLngBounds( new GLatLng(b.getSouthWest().lat(),a.getSouthWest().lng()), new GLatLng(a.getSouthWest().lat(),a.getNorthEast().lng()) );
					} else {
						// south east
						bounds1 = new GLatLngBounds( new GLatLng(b.getSouthWest().lat(),a.getNorthEast().lng()), new GLatLng(a.getNorthEast().lat(),b.getNorthEast().lng()) );
						bounds2 = new GLatLngBounds( new GLatLng(b.getSouthWest().lat(),a.getSouthWest().lng()), new GLatLng(a.getSouthWest().lat(),a.getNorthEast().lng()) );
					}		

				} else {
					if (b.getSouthWest().lng() < a.getSouthWest().lng()) {
						// north west		
						bounds1 = new GLatLngBounds( new GLatLng(a.getSouthWest().lat(),b.getSouthWest().lng()), new GLatLng(b.getNorthEast().lat(),a.getSouthWest().lng()) );
						bounds2 = new GLatLngBounds( new GLatLng(a.getNorthEast().lat(),a.getSouthWest().lng()), new GLatLng(b.getNorthEast().lat(),a.getNorthEast().lng()) );
					} else {
						// north east		
						bounds1 = new GLatLngBounds( new GLatLng(a.getSouthWest().lat(),a.getNorthEast().lng()), new GLatLng(b.getNorthEast().lat(),b.getNorthEast().lng()) );
						bounds2 = new GLatLngBounds( new GLatLng(a.getNorthEast().lat(),a.getSouthWest().lng()), new GLatLng(b.getNorthEast().lat(),a.getNorthEast().lng()) );
					}	
				}

				this.hitDatabase(bounds1, map);
				this.hitDatabase(bounds2, map);
				this.loadedBounds = this.loadedBounds.expand(bounds);
			}
		}
	},

	hitDatabase: function(bounds, map) {
		this.notify(new GeoEvent(GeoEvent.START_LOAD, {}));
		var request = Utils.ajaxRequest(this.server,this.method,
						[bounds.getSouthWest().lng(),bounds.getNorthEast().lng(),
						bounds.getSouthWest().lat(),bounds.getNorthEast().lat() ],
						this.onDatabaseLoad, this);
	},		

	onDatabaseLoad: function(request) {	
		var newobjs = Utils.createObjects(request,this._object_model);
		var newobjs = this.append(newobjs);

		if (newobjs.length > 0) this.notify(new GeoEvent(GeoEvent.NEW_DATA,newobjs));
		this.notify(new GeoEvent(this.nextEvent,this.currentBounds));
		this.notify(new GeoEvent(GeoEvent.END_LOAD, {}));
	},
		
	act: function(action,data) {
		var model = this;
		model.data = data;
		
//		if (model.loaded[action]) {
		
//		} else {
			var pars = 'uid=' + user + '&rnd=' + (new Date().getTime());
			if (action == 'add') pars += '&latitude='+ data.lat() + '&longitude='+ data.lng();
			else pars += '&id='+this.selectedObject.id;

			var myAjax = new Ajax.Updater(
				action + this.typeid + 'screen',
				'/resources/app/views/'+this.typeid+'/'+action+'.php',
				{
					method: 'get',
					parameters: pars,
					evalScripts: true,
					onComplete: function() { 
						model.loaded[action] = true; 
						displayVeil(true); 
//						Element.hide('map'); 
						Element.show(action + model.typeid + 'screen');
						$(action + model.typeid + 'screen').style.zIndex = 20003;
					}
				});
//		}
		this.notify(new GeoEvent(GeoEvent.ACTION), {});
	},
	
	enable: function(en) {
		if (en) {
			if (!(this.enabled)) this.selectid(this.prefix + 'show')
		}
		else {
//			if (this.enabled) this.selectid(this.prefix + 'none');
		}
	}
});


var WorldOverviewCollectionModel = Class.extend(WorldDBCollectionModel, {

	initialize: function(options) {
		$C(WorldDBCollectionModel).initialize.call(this,options);
		
		this.loadedOverviewBounds = null;
		this.loadedDetailBounds = null;
		this.state = '';
		
		this.overviewObjects = [];
	},
		
	appendOverviews: function(newobjs) {
		var model = this;
		newobjs.each(function(node) { model.update(node); } );
		
		this.overviewObjects = this.overviewObjects.concat(newobjs);	
	},

	clearOverviews: function() {
//		this.overviewObjects = [];
	},
	
	move: function(bounds, zoom, map) {
		if (this.enabled) {
			if (zoom < GMap2.STATE_ZOOM_LEVEL) {
				if (this.state != 'overview') {
					this.notify(new GeoEvent(GeoEvent.SHOW_OVERVIEW, {}));
					this.loadedBounds = this.loadedOverviewBounds;
				}
				this.state = 'overview';

				if (this.loadedBounds) {
					if (  (this.loadedBounds.containsBounds(bounds))
					   || (bounds.containsBounds(this.loadedBounds)) 
					   || (!this.loadedBounds.intersects(bounds))     ) {
						this.clearOverviews();
					}

/*					
					if (this.loadedBounds.containsBounds(bounds)) {
						this.hitDatabase(bounds, map);
					}
*/
				}
			} else {
				if (this.state != 'detail') {
					this.loadedBounds = this.loadedDetailBounds;
					this.clearOverviews();
					this.notify(new GeoEvent(GeoEvent.CLEAR_OVERVIEW, {}));
					this.notify(new GeoEvent(GeoEvent.SHOW_DETAIL, {}));
				}
				
				this.state = 'detail'
			}
		}		
		$C(WorldDBCollectionModel).move.call(this,bounds,zoom,map);
	},
		
	hitDatabase: function(bounds, map) {
		this.notify(new GeoEvent(GeoEvent.START_LOAD, {}));

		if (this.state == 'overview') {
			var point_ne = map.fromLatLngToDivPixel(bounds.getNorthEast());
			var point_sw = map.fromLatLngToDivPixel(bounds.getSouthWest());
			var max = Math.floor( ( (point_ne.x - point_sw.x) * (point_sw.y - point_ne.y) ) * this.overview_factor );

			this.hitOverviewDatabase(bounds, max);
		} else {
			this.hitDetailDatabase(bounds);
		}
	},
	
	hitOverviewDatabase: function(bounds, max) {
	
	},
	
	hitDetailDatabase: function(bounds) {
	
	},
	
	onOverviewLoad: function(request) {
		var newobjs = Utils.createObjects(request,this._object_model);
		this.appendOverviews(newobjs);
		
		this.notify(new GeoEvent(GeoEvent.OVERVIEW_DATA, newobjs));
		this.notify(new GeoEvent(GeoEvent.MOVE, this.currentBounds));
		this.notify(new GeoEvent(GeoEvent.END_LOAD, {}));
	}

});

/*
	GeoEvent
*/
var GeoEvent = Object.extend(MVCEvent, {
	MARKER_SELECT: 0,
	SELECT:	1,
	MOVE: 2,
	NEW_DATA: 3,
	SELECT_PROPERTY: 4,
	SELECT_ALL: 5,
	SELECT_NONE: 6,
	DATE_CHANGE: 7,
	ZOOMTO: 8,
	UPDATE: 9,
	REMOVE: 10,
	START_LOAD: 11,
	END_LOAD: 12,
	ENABLED: 13,
	OVERVIEW_DATA: 14,
	CLEAR_OVERVIEW: 15,
	SHOW_OVERVIEW: 16,
	SHOW_DETAIL: 17
});


/*
var WorldMarker = Class.extend(PdMarker, {
	initialize: function(a,b,c) {
		this.superclass = PdMarker;
		this.superclass(a,b,c);
	}
});
*/

$C(PdMarker).onClick = function() {	
	if (this.object.collection) this.object.collection.doSelect(this.object);
	this.doClick();
	
	// Notify
	if (this.object.collection) this.object.collection.notify(new GeoEvent(GeoEvent.MARKER_SELECT,this.object));
}

$C(PdMarker).doClick = function() {		
	if (this.showDetailOnClick && this.detailWinHTML)
		this.showDetailWin();
}

$C(PdMarker).closeDetailWin = function() {	
	if (this.object.listDetail) this.setDetailWinHTML(this.object.listDetail);
	if (this.object.listTooltip) this.setTooltip(this.object.listTooltip);
	
	this.detailOpen = false;
	if (this.detailObject)
	{
		this.setMouseOutEnabled(true);
		this.onMouseOut();
		// GEvent.trigger(this, "mouseout");
	      this.map.getPane(G_MAP_FLOAT_PANE).removeChild(this.detailObject);
		this.detailObject = null;
	}
}


/*
	!! Detail window word-wrap was not functional in Firefox.
*/
$C(PdMarker).showDetailWin = function() {
	
	if (this.detailOpen)
	{
		this.closeDetailWin();
		return;
	}

	this.hideTooltip();
	this.setMouseOutEnabled(false);

	var html = "<table><tr><td>" + this.detailWinHTML + "<\/td><td valign='top'><a class='markerDetailClose' href='' onclick='PdMarkerClose(" + this.internalId + "); return false;'><img src='http://www.google.com/mapfiles/close.gif' width='14' height='13'><\/a><\/td><\/tr><\/table>";

	this.detailOpen = true;

	if  (!this.tooltipText)  
	{
		
		this.ttWidth = 150;
		this.ttHeight = 30;
		setTTPosition(this); // compute ttTop, ttLeft
	}

	var ttPos = latLongToPixel(this.map,this.getPoint(),this.map.getZoom());
	ttPos.y -= Math.floor(40);
	ttPos.x += this.getIcon().iconSize.width / 2;
	
	if (!this.ttTop) {
		this.ttTop = ttPos.y;
		this.ttLeft = ttPos.x; 
	}
	
	this.initDetailWin_GeoBirds(this, ttPos.y, ttPos.x, html);
	PdMarkerAddToExtList(this);
	
}

$C(PdMarker).initDetailWin_GeoBirds = function(theObj, top, left, html) {
	theObj.detailId = "detail" + theObj.internalId;

	var b = document.createElement('div');
	theObj.detailObject = b;

	b.setAttribute('id',theObj.detailId);

	Element.addClassName(b, 'markerDetail');
	
	b.innerHTML = html;
	b.style.top  = top + "px";
	b.style.left = left + "px";

	theObj.map.getPane(G_MAP_FLOAT_PANE).appendChild(b);	
}

/*
	Abstract class!!
*/
WorldModel = Class.extend(Model, {

	initialize: function(options) {
		$C(Model).initialize.call(this,options);
		this.objclass = WorldModel;
	},
	
	getReady: function() {
		var icon = this.getIcon();
		var gpoint = new GLatLng(this.latitude,this.longitude);
		
		this.marker = new PdMarker(gpoint,icon);
		this.marker.added = false;
		this.marker.allowLeftTooltips(false);
		this.marker.object = this;

		this.singleTooltip = this.tooltipText();
		this.singleDetail = this.viewText();

		this.marker.setDetailWinHTML(this.singleDetail);
		this.marker.setTooltip(this.singleTooltip);
	},

	getIcon: function() { 
		return this.objclass.icons[this.type];
	},
	
	tooltipText: function() {
		var text = Utils.apply(this.tooltipTemplate, this);
		if ((this.numcomments) && (this.numcomments == 1)) text = text.replace(/comments/, "comment");
		return text;
	},
	
	viewText: function() {
		var model = this;
		
		var text = '<div class="detailwin" onmouseover="worldapp.map.disableDragging(); return false;" onmouseout="worldapp.map.enableDragging(); return false;" style="width:180px; height:180px; border:none; overflow:auto; margin-bottom: 3px;">';
		text += Utils.apply(this.startViewTemplate, this);
		this.innerTemplates.each(function(node) {
			if (model[node.property]) text += Utils.apply(node.text, model);
		});
		text += Utils.apply(this.endViewTemplate, this);
		text += '</div>';
		
		return text;
	},
	
	zoomTo: function() {
		this.notify(new GeoEvent(GeoEvent.ZOOMTO, this));
	},
	
	update: function() {
		if (this.marker) {
			var map = this.marker.map;	
			map.removeOverlay(this.marker);
		}
		this.getReady();
		this.collection.notify(new GeoEvent(GeoEvent.NEW_DATA, [this]));
		this.notify(new GeoEvent(GeoEvent.UPDATE, this));
	},
	
	tooltipTemplate: '',
	
	startViewTemplate: '',
	
	endViewTemplate: '',
	
	innerTemplates: []
		
});

var ScaffoldHandler = Class.extend({}, {

	initialize: function() {
		this.obj = null;
		this.map = null;
		this.action = '';
		this.type = '';
	},

	populate: function() {
		gpoint = new GLatLng(Number(this.obj.latitude), Number(this.obj.longitude));
	},

	setObject: function(theobj) {
		this.obj = theobj;
		this.populate();
	},

	display: function(vis) {
		if (vis) {
			Element.hide('map');
			Element.show(this.action + this.type + 'screen');
			$(this.action + this.type + 'screen').style.zIndex = 20003;
			displayVeil(true);
		} else {
			$(this.action + this.type + 'screen').style.zIndex = 20000;
			Element.show(this.action + this.type + 'screen');
			Element.show('map');
			displayVeil(false);
		}
	},

	submit: function() {
		return false;
	},
	
	fireUpdate: function() {
		this.obj.update();
	},
	
	showSelects: function() {
		// do nothing!!
	}
});


/******************************************
Do some OO-design work on the veil later!
*******************************************/

var veilOverlay = null;
var VEIL_ZINDEX = 20000;
function displayVeil(sh) {
	if (sh) {
		if (veilOverlay == null) {
			veilOverlay = $("pageveil");

			setVeilWidthPosition();
			addListener(window, "resize", setVeilWidthPosition);
			
			if (isIE) $('dateselect').style.visibility = 'hidden';	
			veilOverlay.style.display = 'block';
		} else {
			veilOverlay.style.display = 'block';
			if (isIE) $('dateselect').style.visibility = 'hidden';	
		}
	} else {
		if (isIE) $('dateselect').style.visibility = 'visible';
		if (veilOverlay) veilOverlay.style.display = 'none';			
	}
}

function setVeilWidthPosition() {

	var ht = $("wrapper").clientHeight - 181;
	var wd = $("wrapper").clientWidth;
	
	veilOverlay.style.top = '-18px';
	veilOverlay.style.left = '-10px';
	
	veilOverlay.style.width = wd + 'px';
	veilOverlay.style.height = ht + 'px';
}

function addListener(obj, evType, fn) {
  if (obj.addEventListener) {
    obj.addEventListener(evType, fn, false);
    return true;
    }
  else if (obj.attachEvent) return obj.attachEvent('on' + evType, fn);
  else return false;
  }
  
function getScroll(){
    var yScroll;

    if (self.pageYOffset) {
        yScroll = self.pageYOffset;
    } else if (document.documentElement && document.documentElement.scrollTop){  // Explorer 6 Strict
        yScroll = document.documentElement.scrollTop; 
    } else if (document.body) {// all other Explorers
        yScroll = document.body.scrollTop;
    }
    return yScroll;
}


function cacheVeil() {
	displayVeil(true);
	displayVeil(false);
}


/****************** end veil stuff **************/


