// Requires JQuery

if(!maps)
	var maps = new Object();

if(window['placemarks'] == 'undefined')
	var placemarks = new Object();

//Object Representing a Simpleview Map
function Map(name,divname,defaultLat,defaultLng,defaultZoom){
	this.defaultSettings = {
		control: null,
		defaultLat: defaultLat,
		defaultLng: defaultLng,
		defaultZoom: defaultZoom,
		defaultPoint: new GLatLng(defaultLat,defaultLng)
	}

	this.settings = {
		control: null,
		defaultLat: defaultLat,
		defaultLng: defaultLng,
		defaultZoom: defaultZoom,
		defaultPoint: new GLatLng(defaultLat,defaultLng)
	}
	
	this.bounds = new GLatLngBounds();
	this.name = name;
	this.gmap = null;

	this.collections = new Object();
	this.defaultCollection = null;

	this.allFilters = new Object();
	this.cancelCentering = false;


	//Create An Alias
	var map = this;

	this.visibleTips = new Array();

	this.sortCollection = function(collection,sortBy){ this.collections[collection].sortBy(sortBy) };
	
	defaultStyle = {
		image: imgroot + '/includes/images/gmaps/marker_icon:label.png',
		shadow: imgroot + '/includes/images/gmaps/marker_shadow.png',
		smimage: imgroot + '/includes/images/gmaps/marker_sm_icon:label.png',
		hover:false,
		iconSize: new GSize(24.0, 35.0),
		shadowSize: new GSize(42.0, 35.0),
		iconAnchor: new GPoint(11.0, 34.0),
		infoWindowAnchor: new GPoint(14.0, 17.0)
	};
	
	/*orangeStyle = jQuery.extend(false, defaultStyle, {image: imgroot + '/includes/images/gmaps/org/marker_icon:label.png', smimage: imgroot + '/includes/images/gmaps/org/marker_sm_icon:label.png'});
	blueStyle = jQuery.extend(false, defaultStyle, {image: imgroot + '/includes/images/gmaps/blu/marker_icon:label.png', smimage: imgroot + '/includes/images/gmaps/blu/marker_sm_icon:label.png'});
	greenStyle = jQuery.extend(false, defaultStyle, {image: imgroot + '/includes/images/gmaps/grn/marker_icon:label.png', smimage: imgroot + '/includes/images/gmaps/grn/marker_sm_icon:label.png'});
	redStyle = jQuery.extend(false, defaultStyle, {image: imgroot + '/includes/images/gmaps/red/marker_icon:label.png', smimage: imgroot + '/includes/images/gmaps/red/marker_sm_icon:label.png'});
	yellowStyle = jQuery.extend(false, defaultStyle, {image: imgroot + '/includes/images/gmaps/ylw/marker_icon:label.png', smimage: imgroot + '/includes/images/gmaps/ylw/marker_sm_icon:label.png'});
	tealStyle = jQuery.extend(false, defaultStyle, {image: imgroot + '/includes/images/gmaps/teal/marker_icon:label.png', smimage: imgroot + '/includes/images/gmaps/teal/marker_sm_icon:label.png'});
	pinkStyle = jQuery.extend(false, defaultStyle, {image: imgroot + '/includes/images/gmaps/pink/marker_icon:label.png', smimage: imgroot + '/includes/images/gmaps/pink/marker_sm_icon:label.png'});
	starStyle = jQuery.extend(false, defaultStyle, {image: imgroot + '/includes/images/gmaps/marker_star.png', smimage: imgroot + '/includes/images/gmaps/marker_sm_star.png'});*/
	itinStyle = jQuery.extend(false, defaultStyle, {image: imgroot + '/includes/images/gmaps/itin_icon:label.png', smimage: imgroot + '/includes/images/gmaps/itin_sm_icon:label.png', shadow: imgroot + '/includes/images/gmaps/itin_shadow.png'});

	this.initCustomIcons = function(){
		this.iconStyles = {
			'default': defaultStyle,
			'itin': itinStyle,
			'custom': defaultStyle
			/*
			'orange': orangeStyle,
			'blue': blueStyle,
			'green': greenStyle,
			'red': redStyle,
			'yellow': yellowStyle,
			'teal': tealStyle,
			'pink': pinkStyle,
			'star': starStyle
			*/
		};
	}

	//Initialize Map
	this.create = function(useClustering){
		logit('Called: create');
		this.initCustomIcons();
		map.div = $("#" + divname);
		map.gmap = new GMap2(map.div.get(0));

		if(!this.cancelCentering){
			map.gmap.setCenter(map.settings.defaultPoint);
			map.gmap.setZoom(map.settings.defaultZoom);
		}

		if(map.settings.control != null){
			map.control = map.settings.control;
			map.gmap.addControl(map.control);
		}
		map.gmap.setMapType(G_HYBRID_MAP);
		map.gmap.disableScrollWheelZoom();
		if(useClustering){
			this.useClustering = true;
			this.zoomGroups = new Array();
			logit('Initializing zoomgroups');
			this.grouped = new OverlayCollection('grouped',1000);
			this.grouped.useLabels = false;
			this.grouped.labelType = 'Numeric';
			this.grouped.getIconStyle = function(placemark){return 'defaultblackfolder';};
			this.grouped.clickFunction = function(marker){return true;}
			this.grouped.getInfoHTML = function(placemark){return placemark.numPins + ' properties were found at this location.  Click <a href="javascript:zoomToGroup(\'' + map.name + '\',' + placemark.groupIndex + ')">here</a> to zoom in on this group';};
			
			this.grouped.getToolTip = function(placemark){
				return 'Click to Zoom In (' + placemark.numPins + ')';
			}
			
			this.addCollection(this.grouped);
		}
		else
			this.useClustering = false;

		//Register Map with main array
		maps[map.name] = map;
		/*
		GEvent.addListener(this.gmap, "zoomend", function() { 
			if(this.allFilters.zoomFilter && !this.allFilters.zoomFilter.cancelled){
				this.allFilters.zoomFilter.run();
			}
		}.bind(this));
		*/
		logit('End: create');
	}
	
	this.removeOverlay = function(overlay){
		if(overlay.closeInfoWindow)
			overlay.closeInfoWindow();
		this.gmap.removeOverlay(overlay);
		if(overlay.tooltip){
			this.gmap.removeOverlay(overlay.tooltip);
		}
	}
	
	this.addCollection = function(oc,isDefault){
		logit('Called: addCollection: ' + oc.name);
		this.collections[oc.name] = oc;
		if(isDefault || this.defaultCollection == null)
			this.defaultCollection = oc;
		oc.map = this;
		logit('End: addCollection: ' + oc.name);
		return oc;
	}

	//Set Current Maps Control
	this.setControl = function(control){
		if(map.settings.control)
			map.gmap.removeControl(map.settings.control);

		if(control)
			map.gmap.addControl(control);
		this.settings.control = control;
	}

	this.setCenter = function(){
		logit('Called: setCenter');
		map.bounds = new GLatLngBounds();
		var p;
		for (var i in this.collections){
			var c = this.collections[i];
			map.bounds = addBounds(map.bounds,c.calcBounds());
		}
		if(map.bounds.isEmpty()){
			p = map.settings.defaultPoint;
		}
		else{
			p = map.bounds.getCenter();
		}		
		map.gmap.setCenter(p);
		logit('End: setCenter');
	}
	
	this.getBounds = function(){
		var bounds = new GLatLngBounds();
		for (var i in this.collections){
			var c = this.collections[i];
			var cbounds = c.calcBounds();
			bounds = addBounds(bounds,cbounds);
		}
		return bounds;
	}
	
	this.setBoundsCenterAndZoom = function(){
		logit('Called: setBoundsCenterAndZoom');
		map.bounds = this.getBounds();
		if(!map.bounds.isEmpty()){
			var p = map.bounds.getCenter();
			var z = map.gmap.getBoundsZoomLevel(map.bounds);
			if(map.widget && placemarks[map.widget.args.mapdata].length == 1)z -= 3;
		}
		else{
			var p = map.settings.defaultPoint;
			var z = parseInt(map.settings.defaultZoom);
		}
		map.gmap.setCenter(p);
		z = Math.min(z, 17);
		if(this.allFilters.zoomFilter)
			this.allFilters.zoomFilter.cancelled = true;
		map.gmap.setZoom(z);
		if(this.allFilters.zoomFilter)
			this.allFilters.zoomFilter.cancelled = false;
		logit('End: setBoundsCenterAndZoom');
	}

	this.removeOverlays = function(collection){
		logit('Called: removeoverlays - ' + collection.name);
		if(collection.name == 'grouped')
			logit('removing overlays for: ' + collection.name);
		for(var i in collection.data){
			var p = collection.data[i];
			if(p.marker){
				p.marker.closeInfoWindow();
				//GEvent.removeListeners();
				this.removeOverlay(p.marker);
				//delete p.marker;
				logit('Removing Marker for: ' + p.name);
			}
		}
		logit('End: removeoverlays - ' + collection.name);		
	}


	//Remove all placemarks from the map
	this.removePlacemarks = function(collection,cancelHandlers){
		logit('Called: removeplacemarks - ' + collection.name);
		if(this.useClustering)
			this.ungroupMarkers();
		this.removeOverlays(collection);
		collection.clearAll();
		if(!cancelHandlers)
			collection.execHandler('addremove','removeplacemarks');
		logit('End: removeplacemarks - ' + collection.name);
	}

	this.removePlacemark = function(placemark, collection, cancelHandlers){
//		logit('Called: removePlacemark - ' + placemark.name + ' - ' + collection.name);
		if(!collection)
			collection = this.defaultCollection;

		if(placemark.marker){
			this.removeOverlay(placemark.marker);
		}
		collection.clear(placemark);
		
		if(!cancelHandlers){
			collection.execHandler('addremove','removeplacemark');
		}
//		logit('End: removePlacemark - ' + placemark.name + ' - ' + collection.name);		
	}

	//Add a Simple Marker
	this.addPlacemark = function(placemark,collection,cancelHandlers){
		if(!collection)
			collection = map.defaultCollection;

		collection.set(placemark);

		if(!cancelHandlers){
			collection.execHandler('addremove','addplacemark');
		}
	}

	this.showPlacemark = function(placemark,cancelHandlers){
		var marker = placemark.marker;
		var collection = placemark.collection;
		map.bounds.extend(marker.getPoint());
		map.gmap.addOverlay(marker);
		if(placemark.icon.hover)
		{
			GEvent.addListener(marker,"mouseover",function(){
					var imgsrc = this.getIcon().image;
					var newsrc = imgsrc.replace('_hover','');
					var newsrc = newsrc.replace('.png','');
					var components = newsrc.split('_');
					if(components.length > 1){
						label = components[components.length-1];
						components = components.splice(components,components.length-1,1);
						var str = components.join('_');
						newsrc = str + '_hover' + '_' + label + '.png';
					}
					else
						newsrc = newsrc + '_hover.png';
					this.setImage(newsrc);	
					//GLog.write(newsrc);
				});
	
			GEvent.addListener(marker,"mouseout",function(){
					var imgsrc = this.getIcon().image;
					var newsrc = imgsrc.replace('_hover','');
					this.setImage(newsrc);	
					//GLog.write(newsrc);
				});
		}
		if(marker.tooltip){
			logit('adding tooltip - ' + marker.title);
			map.gmap.addOverlay(marker.tooltip);
			
			GEvent.addListener(marker, "mouseover", 
				function() {
					this.tooltip.show();
					//map.visibleTips.push(this.tooltip);
					return false;
				}
			)
			
			GEvent.addListener(marker, "mouseout",
				function() {
					this.tooltip.hide();
					//$P(map.visibleTips).map(function(itm){itm.hide()});
					//map.visibleTips = new Array();
					return false;
				}
			)
		}
		if(placemark.name.indexOf('group_') >= 0){
			GEvent.addListener(marker, "click", function() {
				map.zoomToGroup(marker.placemark.groupIndex);
			});
			
		}
		else if(marker.myInfoHTML){
			GEvent.addListener(marker, "click", function() {
				map.gmap.setCenter(marker.getPoint());
				marker.openInfoWindowHtml(marker.myInfoHTML);
				collection.execHandler('openinfobubble',placemark);
			});
		}
		else
		{
			GEvent.addListener(marker, "click", function() {
			maps[map.widget.args.mapdata].goToPlacemark(placemark);
			});
		}

		if(!cancelHandlers)
			collection.execHandler('showhide','showplacemark');
//		logit('End: showplacemark - ' + placemark.name);
	}
	this.sendBack = function(){
		if(!this.zindex)
			this.zindex = 100;
		this.zindex++;
		return this.zindex;
	}

	//Ensures all placemarks in this array have a marker created
	this.preparePlacemarks = function(placemarks,oc){
		for (var i = 0; i < placemarks.length; i++){
			var placemark = placemarks[i];

			if(oc.useLabels){
				placemark.label = (oc.labelType == 'Alpha' ? chr(i + asc('A')) :  (placemark.type && placemark.type == 'group') ? placemark.numPins : (i + 1));
			}
			else{
				placemark.label = '';
			}
			
			//Just trying to be careful with this one
			if(placemark.marker){
				logit('found marker for: ' + placemark.name + ' - removing');
			} else {
				//Assign icon
				placemark.icon = this.createCustomIcon(oc.getIconStyle(placemark),placemark.label);
				
				//Get Text for ToolTip
				placemark.tooltip = oc.getToolTip(placemark);
				placemark.marker = new GMarker(new GLatLng(placemark.latitude,placemark.longitude),{icon:placemark.icon,zIndexProcess:$.shove(this.sendBack, this)});
				placemark.marker.title = placemark.tooltip;
				//placemark.marker.tooltip = new Tooltip(placemark.marker,placemark.tooltip,4);
	
				//Add Reverse Lookup - Memory leak Issue?
				placemark.marker.placemark = placemark;
				placemark.marker.myInfoHTML = oc.getInfoHTML(placemark);
			}
			if(oc.name != 'grouped' && !placemark.isClustered){
				placemark.isClustered = false;
				placemark.cluster = null;
			}
			logit('setting marker for: ' + placemark.name);
		}
		return placemarks;
	}

	this.showPlacemarks = function(oc,page,cancelHandlers){
		logit('Called: Showplacemarks - ' + oc.name);
		this.removeOverlays(oc);
		if(!page)
			page = 1;

		oc.currPage = page;

		var placemarks = oc.getPage(page);

		//Prepare placemarks
		this.preparePlacemarks(placemarks,oc);

		//Set the center and zoom of map
		if(!this.cancelCentering){
			map.setBoundsCenterAndZoom();
		} else if (typeof map.circle != 'undefined') {
			map.gmap.setCenter(map.circle.getBounds().getCenter(),map.gmap.getBoundsZoomLevel(map.circle.getBounds()));
		}

		//Convert this page to groups where necessary
		if(this.useClustering && oc != this.grouped){
			this.groupPlacemarks(placemarks);
			this.showGrouped();
		}

		//Show them all
		for (var i = 0; i < placemarks.length; i++){
			var placemark = placemarks[i];
			if(!placemark.isClustered){
				var p = new GLatLng(placemark.latitude,placemark.longitude);
				placemark.icon = this.createCustomIcon(oc.getIconStyle(placemark),placemark.label);
				var m = new GMarker(new GLatLng(placemark.latitude,placemark.longitude),{icon:placemark.icon,zIndexProcess:$.shove(this.sendBack, this)});
				m.title = placemark.tooltip;
				//m.tooltip = new Tooltip(m,placemark.tooltip,4);
				m.myInfoHTML = oc.getInfoHTML(placemark);
				//Add Reverse Lookups
				m.placemark = placemark;
				placemark.marker = m;
				this.showPlacemark(placemark,true);
			}
		}

		if(!cancelHandlers)
			oc.execHandler('showhide','showplacemarks');
		logit('End: Showplacemarks - ' + oc.name);
	}

	this.drawGrid = function(){
		var cols = 13;
		var rows = 7;
		var groupThreshold = 3;
		var width = this.gmap.getSize().width;
		var height = this.gmap.getSize().height;
		var stepX = width / cols;
		var stepY = height / rows;
		var markerarray = new Array();
		var overloaded = new Array();


		if(this.gridoverlays){
			$P(this.gridoverlays).map(
				function(itm){
					this.removeOverlay(itm);
				}.bind(this)
			);
			this.gridoverlays = new Array();
		}
		else
			this.gridoverlays = new Array();

		for (var i = 0; i < cols; i++){
			var xlow = i * stepX;
			var xhigh = (i+1) * stepX;
			for (var j =0; j < rows; j++){
				var ylow = j * stepY;
				var yhigh = (j+1) * stepY;
				var points = new Array();
				points.push(this.gmap.fromContainerPixelToLatLng(new GPoint(xlow,ylow)));
				points.push(this.gmap.fromContainerPixelToLatLng(new GPoint(xhigh,ylow)));
				points.push(this.gmap.fromContainerPixelToLatLng(new GPoint(xhigh,yhigh)));
				points.push(this.gmap.fromContainerPixelToLatLng(new GPoint(xlow,yhigh)));
				points.push(this.gmap.fromContainerPixelToLatLng(new GPoint(xlow,ylow)));
				var box = new GPolygon(points,'#0000ff', 1, 1, '#0000ff', 0.2);
				this.gmap.addOverlay(box);
				this.gridoverlays.push(box);
			}
		}
	}

	this.groupPlacemarks = function(placemarks){
		logit('Called: groupPlacemarks');
		var cols = 13;
		var rows = 7;
		var groupThreshold = 3;
		var width = this.gmap.getSize().width;
		var height = this.gmap.getSize().height;
		var stepX = width / cols;
		var stepY = height / rows;
		var markerarray = new Array();
		var overloaded = new Array();
//		this.drawGrid();

		for(var i = 0; i < cols; i++){
			for (var j = 0; j < rows; j++){
				var idx = j * cols + i;
					markerarray[idx] = {
					markers:new Array(),
					cnt:0,
					totalX:0,
					totalY:0,
					centerX: i * stepX + (stepX / 2.0),
					centerY: j * stepY + (stepY / 2.0)
				}
			}
		}

		//Get the current page and drop all placemarks into their grid blocks, update summary stats
		for (var i = 0; i < placemarks.length; i++){
			var p = placemarks[i];
			var loc = this.gmap.fromLatLngToContainerPixel(p.marker.getPoint());
			var posx = Math.floor(loc.x / stepX);
			var posy = Math.floor(loc.y / stepY);
			var idx = posy * cols + posx;
			if(idx >= 0 && idx < markerarray.length){
				markerarray[idx].cnt++;
				markerarray[idx].markers.push(p);
				markerarray[idx].totalX += loc.x;
				markerarray[idx].totalY += loc.y;
			}
		}
		
		//Find the overloaded grid blocks
		for (var i = 0; i < (rows * cols); i++){
			if(markerarray[i].cnt > groupThreshold){
				markerarray[i].avgX = markerarray[i].totalX / markerarray[i].cnt;
				markerarray[i].avgY = markerarray[i].totalY / markerarray[i].cnt;
				markerarray[i].position = this.gmap.fromContainerPixelToLatLng(new GPoint(markerarray[i].avgX,markerarray[i].avgY));
				overloaded.push(markerarray[i]);
			}
		}

		//Mark these placemarks as grouped
		for(var i = 0; i < overloaded.length; i++){
			var cluster = overloaded[i];
			for (var j = 0; j < cluster.markers.length; j++){
				cluster.markers[j].isClustered = true;
				cluster.markers[j].cluster = cluster;
				cluster.groupIndex = i;
			}
		}

		this.zoomGroups = overloaded;

		logit('End: groupPlacemarks');		
		
	}

	this.ungroupMarkers = function(){
		logit('Called: ungroupmarkers');
		logit('this.grouped.length is: ' + this.grouped.length + ' before ungroup markers');		
		this.removeOverlays(this.grouped);
		for(var i = 0; i < this.zoomGroups.length; i++){
			var cluster = this.zoomGroups[i];
			for (var j = 0; j < cluster.markers.length; j++){
				var p = cluster.markers[j];
				p.isClustered = false;
				p.cluster = null;
			}
		}
		this.zoomGroups = new Array();
		this.grouped.clearAll();
		logit('this.grouped.length is: ' + this.grouped.length + ' after ungroup markers');
		logit('End: ungroupmarkers');			
	}
	
	this.showGrouped = function(){
		logit('Called: showgrouped - ' + this.zoomGroups.length);
		var groups = this.zoomGroups;
		var backup = this.cancelCentering;
		this.cancelCentering = true;		
		for (var i = 0; i < groups.length; i++){
			var pnt = new GPoint(groups[i].centerX,groups[i].centerY);
			var translated = this.gmap.fromContainerPixelToLatLng(pnt);
			var p = {
				prikey:'group_' + i,
				name:'group_' + i + ' - ' + groups[i].cnt,
				iconStyle: 'defaultblackfolder',
				type: 'group',
				numPins: groups[i].cnt,
				latitude: translated.lat(),
				longitude: translated.lng(),
				groupIndex: i,
				point: translated,
				zoomGroup: groups[i]
			};
			groups[i].placemark = p;
			this.addPlacemark(p,this.grouped,true);
		}

		this.showPlacemarks(this.grouped,1,true);

		this.cancelCentering = backup;
		logit('End: showgrouped - ' + groups.length);		
	}

	this.zoomToGroup = function(groupIndex){
		logit('Called: zoomtogroup - ' + groupIndex);
		var group = this.zoomGroups[groupIndex];
		group.placemark.marker.closeInfoWindow();
		var bounds = new GLatLngBounds();
		logit('zooming to group based on ' + group.markers.length + ' markers');
		var minlat = 1000;
		var maxlat = -1000;
		for (var i = 0; i < group.markers.length; i++){
			var p = group.markers[i];
			var pnt = new GLatLng(p.latitude,p.longitude);
			bounds.extend(pnt);
		}
		var p = bounds.getCenter();
		var z = map.gmap.getBoundsZoomLevel(bounds);
		logit('setting zoom to: ' + z + ' from ' + map.gmap.getZoom());
		this.gmap.setCenter(p);
		this.gmap.setZoom(z);
//		if(this.allFilters.zoomFilter)
//			this.allFilters.zoomFilter.run();
		logit('End: zoomtogroup - ' + groupIndex);
	}
	
	this.zoomAndGoTo = function(placemark){
		var cluster = placemark.cluster;
		this.zoomToGroup(cluster.groupIndex);
		goToPlacemark(placemark.prikey);
	}

	this.addPlacemarks = function(placemarks,collection,cancelHandlers){
		logit('Called: addPlacemarks - ' + collection.name);
		for (var i = 0; i < placemarks.length; i++){
			var g = placemarks[i];
			//Add Reverse Lookups
			map.addPlacemark(g,collection,true);
		}
		if(!cancelHandlers)
			collection.execHandler('addremove','addplacemarks');
		logit('End: addPlacemarks - ' + collection.name);
	}

	this.createCustomIcon = function (style,label){
		var iconStyle = clone(this.iconStyles[style]);
		iconStyle.image = iconStyle.image.replace('icon:label',label);
		if (iconStyle.smimage) iconStyle.smimage = iconStyle.smimage.replace('icon:label',label);
		var icon = new GIcon(iconStyle);
		return icon;
	}
	
	this.registerFilter = function(name,filter){
		this.allFilters[name] = filter;
	}
	
	this.initFilters = function(){
		for(var i in this.allFilters){
			this.allFilters[i].run();
		}
	}
	
	this.getVisiblePlacemarks = function(){
		var arr = new Array();
		for (var i in this.collections){
			var p = this.collections[i].getPage(this.collections[i].currPage);
			for(var j = 0; j < p.length; j++){
				arr.push({name:p[j].name,collection:this.collections[i].name,prikey:p[j].prikey});
			}
		}
		return arr;
	}
	//Finds the edges of the icons and sets an adjust zoom level flag if the icon will get cut off
	this.adjustForIcons = function(marker){
		var container = this.gmap.getContainer();
		var containerY = container.scrollHeight;
		var containerX = container.scrollWidth;

		//LatLng of Marker
		var latlng = marker.getLatLng();

		//Convert to Pixel Object
		var divpixels = this.gmap.fromLatLngToContainerPixel(latlng);

		//Placement of icon (in pixels)
		var y = divpixels.y;
		var x = divpixels.x;

		//Width and Height of icon
		var iconWidth = marker.getIcon().iconSize.width;
		var iconHeight = marker.getIcon().iconSize.height;

		//Offset of icon from plotted point
		var anchorWidth = marker.getIcon().iconAnchor.x;
		var anchorHeight = marker.getIcon().iconAnchor.y;

		//Location of top of image (in pixels)
		var iconTop = y - anchorHeight;	
		//Location of bottom of image (in pixels)
		var iconBottom = y + iconHeight - anchorHeight;

		//Location of left edge of icon
		var iconLeft = x - anchorWidth;
		//Location of right edge of icon		
		var iconRight = x + iconWidth + anchorWidth;
		var zoomOut = (iconTop < 0 || iconBottom > containerY || iconLeft < 0 || iconRight > containerX);
		if(zoomOut)
			this.zoomAdjust = -1;
		return zoomOut;
	}
}

function SVToolbarControl(options) {
	this.toggleView = options.toggleView;
}

// To "subclass" the GControl, we set the prototype object to
// an instance of the GControl object
SVToolbarControl.prototype = new GControl();

// Creates a one DIV for each of the buttons and places them in a container
// DIV which is returned as our control element. We add the control to
// to the map container and return the element for the map class to
// position properly.
SVToolbarControl.prototype.initialize = function(map) {
	var tbcontainer = document.createElement("div");
	$(tbcontainer).html("<a href=\"#\" class=\"max\"><img src=\"" + imgroot + "/includes/images/gmaps/max.png\" alt=\"view large map\" /></a>");
	this.max = false;
	jQuery(".max", tbcontainer).click(this.toggleView);
	this.toggle = function(){
		$("img", tbcontainer).attr("src", imgroot + "/includes/images/gmaps/" + (this.max ? "max" : "min") + ".png");
		this.max = !this.max;
	};
	map.getContainer().appendChild(tbcontainer);
	return tbcontainer;
}

SVToolbarControl.prototype.getDefaultPosition = function() {
	return new GControlPosition(G_ANCHOR_TOP_RIGHT, new GSize(10, 10));
}

function SVTypeControl(options) {
}

// To "subclass" the GControl, we set the prototype object to
// an instance of the GControl object
SVTypeControl.prototype = new GControl();

// Creates a one DIV for each of the buttons and places them in a container
// DIV which is returned as our control element. We add the control to
// to the map container and return the element for the map class to
// position properly.
SVTypeControl.prototype.initialize = function(map) {
	var controlContainer = document.createElement("div");
	$(controlContainer).html("<a href=\"#\" class=\"map add5pxLeft\"><img src=\"" + imgroot + "/includes/images/gmaps/map.png\" alt=\"map\" /></a>");
	$(controlContainer).append("<a href=\"#\" class=\"satellite add5pxLeft\"><img src=\"" + imgroot + "/includes/images/gmaps/satellite.png\" alt=\"satellite\" /></a>");
	$(controlContainer).append("<a href=\"#\" class=\"hybrid add5pxLeft\"><img src=\"" + imgroot + "/includes/images/gmaps/hybrid.png\" alt=\"hybrid\" /></a>");
	$(".map", controlContainer).click(function(){map.setMapType(G_NORMAL_MAP);return false});
	$(".satellite", controlContainer).click(function(){map.setMapType(G_SATELLITE_MAP);return false});
	$(".hybrid", controlContainer).click(function(){map.setMapType(G_HYBRID_MAP);return false});
	map.getContainer().appendChild(controlContainer);
	return controlContainer;
}

SVTypeControl.prototype.getDefaultPosition = function() {
	return new GControlPosition(G_ANCHOR_TOP_RIGHT, new GSize(10, 10));
}

function SVNearbyControl(options) {
}

// To "subclass" the GControl, we set the prototype object to
// an instance of the GControl object
SVNearbyControl.prototype = new GControl();

// Creates a one DIV for each of the buttons and places them in a container
// DIV which is returned as our control element. We add the control to
// to the map container and return the element for the map class to
// position properly.
SVNearbyControl.prototype.initialize = function(map) {
	var container = document.createElement("div");
	$(container).html("<select name=\"categories\"><option>find nearby attractions</option></select>");
	jQuery(".max", container).click(this.toggleView);
	
	map.getContainer().appendChild(container);
	return container;
}

// By default, the control will appear in the top left corner of the
// map with 7 pixels of padding.
SVNearbyControl.prototype.getDefaultPosition = function() {
  return new GControlPosition(G_ANCHOR_TOP_RIGHT, new GSize(10, 10));
}



function SVZoomControl(map) {
	this.map = map;
}

// To "subclass" the GControl, we set the prototype object to
// an instance of the GControl object
SVZoomControl.prototype = new GControl();

// Creates a one DIV for each of the buttons and places them in a container
// DIV which is returned as our control element. We add the control to
// to the map container and return the element for the map class to
// position properly.
SVZoomControl.prototype.initialize = function(map) {
	html = "<div class=\"zoomContainer\"><a href=\"#\" class=\"zoomIn\"><img src=\"" + imgroot + "/includes/images/gmaps/zoomIn.png\" alt=\"zoom in\" class=\"add5pxBottom\" /></a><br>";
	html += "<a href=\"#\" class=\"zoomOut\"><img src=\"" + imgroot + "/includes/images/gmaps/zoomOut.png\" alt=\"zoom out\" /></a></div>";
	mapcontainer = jQuery(map.getContainer()).append(html);
	jQuery(".zoomIn", mapcontainer).click(function(){map.zoomIn(); return false;});
	jQuery(".zoomOut", mapcontainer).click(function(){map.zoomOut(); return false;});
	return jQuery(".zoomContainer", mapcontainer).get(0);
}

// By default, the control will appear in the top left corner of the
// map with 7 pixels of padding.
SVZoomControl.prototype.getDefaultPosition = function() {
  return new GControlPosition(G_ANCHOR_TOP_LEFT, new GSize(10, 10));
}




/*************************************************
	General Utility Functions
**************************************************/

function addBounds(b1,b2){
	if (b1.isEmpty())
		return b2;
		
	if(b2.isEmpty())
		return b1;

	var southWest = b1.getSouthWest();  
	var northEast = b1.getNorthEast();
	var b3 = new GLatLngBounds();
	b3.extend(southWest);
	b3.extend(northEast);
	var southWest = b2.getSouthWest();  
	var northEast = b2.getNorthEast();
	b3.extend(southWest);
	b3.extend(northEast);
	return b3;
}

function placemarkDistance(p1,p2){
	return distance(parseFloat(p1.latitude),parseFloat(p1.longitude),parseFloat(p2.latitude),parseFloat(p2.longitude));
}

function distance(lat1,lon1,lat2,lon2){
	var R = 6371; // km
	var kpm = 1.609344;  //km per mile
	var dLat = (lat2-lat1).toRad();
	var dLon = (lon2-lon1).toRad(); 
	var a = Math.sin(dLat/2) * Math.sin(dLat/2) +
			Math.cos(lat1.toRad()) * Math.cos(lat2.toRad()) * 
			Math.sin(dLon/2) * Math.sin(dLon/2); 
	var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a)); 
	var d = (R * c) / kpm;
	return d;
}

function parsePlacemarkData(str,p){
	var fieldlist = ['name','prikey','weburl','addr1','addr2','city','state','zip','phone','latitude','longitude'];
	
	if(p.itinerary)
	{
		str = str.replace('placemark:itinerary','<span id="itin_placemark:prikey"><span class="itinAdd" style="display:none"><a class="bold floatR add10pxRight" href="#" onclick="return gmapsAddItin(placemark:prikey);">Add to Itinerary</a></span><span class="itinAdded bold floatR add10pxRight">Added to Itinerary</span></span>');
		str = str.replace('list:itinerary','<span id="itin_placemark:prikey"><span class="itinAdd" style="display:none"><a href="#" onclick="return gmapsAddItin(placemark:prikey);">Add to Itinerary</a></span><span class="itinAdded">Added to Itinerary</span></span>');
	}
	else
	{
		str = str.replace('placemark:itinerary','<span id="itin_placemark:prikey"><span class="itinAdd"><a class="bold floatR add10pxRight" href="#" onclick="return gmapsAddItin(placemark:prikey);">Add to Itinerary</a></span><span class="itinAdded bold floatR add10pxRight" style="display:none">Added to Itinerary</span></span>');
		str = str.replace('list:itinerary','<span id="itin_placemark:prikey"><span class="itinAdd"><a href="#" onclick="return gmapsAddItin(placemark:prikey);">Add to Itinerary</a></span><span class="itinAdded" style="display:none">Added to Itinerary</span></span>');
	}
	
	for (var i = 0; i < fieldlist.length; i++){
		var searchstr = 'placemark:' + fieldlist[i];
		var replacewith = String(p[fieldlist[i]]).substr(0,300);
		while (str.indexOf(searchstr) > 0 && searchstr != replacewith){
			str = str.replace(searchstr,replacewith);
		}
	}
	

	var searchstr = 'placemark:description';
	if(p.description.length > 0){
		var replacewith = p.description;
	}
	else
		var replacewith = '*No Description Available*';

		while (str.indexOf(searchstr) > 0 && searchstr != replacewith){
			str = str.replace(searchstr,replacewith);
		}


	
	if(p.logofile){
		str = str.replace('placemark:logofile', p.logofile.length > 0 ? p.logofile  : '../listing_nophoto.jpg');
	}
	else
		str = str.replace('placemark:logofile', '../listing_nophoto.jpg');
	
	//Only available when the marker is visible on the map
	if(p.icon){
		str = str.replace('placemark:iconimage',p.icon.image);
		str = str.replace('placemark:sm_iconimage',p.icon.smimage);
	}
	if(p.distance){
		str = str.replace('placemark:distance',p.distance.toFixed(2));
	}
	
	if (p.weburl.length > 0)
		str = str.replace('placemark:website_redirect', '<a href=\"' + imgroot + '/includes/redirects/webcount.cfm?listingID=' + String(p.prikey) + '\" target=\"_blank\">Visit Website</a><br>');
	else
		str = str.replace('placemark:website_redirect', '');
		
	var addr = new Address();
	try{
		addr.loadFromObject(p);
		str = str.replace('placemark:address',addr.toHTMLString());
	}
	catch(ex){
		alert(ex);
	}


	return str;
}

function chr(num){
	return String.fromCharCode(num);
}

function asc(str){
	return str.charCodeAt(0);
}

function clone(obj){
    if(obj == null || typeof(obj) != 'object')
        return obj;

    var temp = new obj.constructor(); // changed (twice)
    for(var key in obj)
        temp[key] = clone(obj[key]);

    return temp;
}

Number.prototype.toRad = function() {  // convert degrees to radians
  return this * Math.PI / 180;
}

//Itinerary Add Function
function gmapsAddItin(listingid)
{
	ajaxAddItinJQuery(imgroot, listingid, '');
	return false;
}

//Itinerary Function
function updatePlacemarkItinerary(prikey){
	var p;
	for (var i in placemarks){
		p = findPlacemark(i,prikey);
		if(p != null)
		{
			p.itinerary = !p.itinerary;
			/*if(p.marker && p.collection)
				p.marker.myInfoHTML = p.collection.getInfoHTML(p);
			if(p.itinerary)
			{
				m.removePlacemark(p,overlays);
				m.addPlacemark(p,itineraryOverlays);
			}
			else
			{
				m.removePlacemark(p,itineraryOverlays);
				m.addPlacemark(p,overlays);
				runFilters();
			}
			overlays.sortBy("name");
			m.showPlacemarks(itineraryOverlays,1);
			m.showPlacemarks(overlays,overlays.currPage);*/
			break;
		}
	}
}

function zoomToGroup(mapname,groupIndex){
	maps[mapname].zoomToGroup(groupIndex);
}

var tabdepth = 0;

function logit(str){
	//return;
	var outstr = str;
	if (str.indexOf('End') == 0){
		tabdepth -= 1;
	}
	for (var i = 0; i < tabdepth; i++){
		outstr = '--' + outstr;
	}
	if(typeof(console) != "undefined")
		console.info(outstr);
	if(str.indexOf('Called') == 0){
		tabdepth += 1;
	}
}

//Cleanup to mitigate memory leaks resulting from cicular dom references and use of closures
$(window).unload(function(){GUnload();});

// jquery adaptation for Function.bind- http://www.quirkey.com/blog/2009/02/25/switched-to-jquery/
$.extend({shove:function(fn, object) {return function() {return fn.apply(object, arguments);}}});