/**
 **/
 $(document).ready(function(){
   // City Menu
   $("#city-select").hide();
   $("#nav .city-menu").hover(function(){
     $("#city-select").show();
   },
   function(){
     //$("#city-select").hide();
   });

   $("#city-select").hover(function(){
   },
   function(){
     $("#city-select").hide();
   }); 
}); 

jQuery.fn.centerScreen = function(loaded) { 
  var obj = this; 
  if(!loaded) { 
    obj.css('top', ($(window).height()*0.3)-(this.height()*0.3)); 
    obj.css('left', $(window).width()/2-this.width()/2); 
    $(window).resize(function() { obj.centerScreen(!loaded); }); 
  } else { 
    obj.stop(); 
    obj.animate({ 
      top: $(window).height()/2-this.height()/2, 
      left: $(window).width()/2-this.width()/2}, 200, 'linear'); 
  } 
};

// Ajax activity indicator bound 
// to ajax start/stop document events
$(document).ajaxStart(function(ajaxevent){ 
	$('#ajaxBusy').show().centerScreen(); 
});
$(document).ajaxStop(function(){ 
	$('#ajaxBusy').hide();
});
$(document).ajaxError(function(){ 
	$('#ajaxBusy').hide();
});

/*
	MODIFIED ClusterMarker Version 1.3.2
	
	A marker manager for the Google Maps API
	http://googlemapsapi.martinpearman.co.uk/clustermarker
	
	Copyright Martin Pearman 2008
	Last updated 29th September 2008

	This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.

	This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more details.

	You should have received a copy of the GNU General Public License along with this program.  If not, see <http://www.gnu.org/licenses/>.
	
*/

function ClusterMarker($map, $options){
	this._map=$map;
	this._mapMarkers=[];
	this._iconBounds=[];
	this._clusterMarkers=[];
	this._eventListeners=[];
	this.moveend_listener=undefined;
	this.zoomend_listener=undefined;
	this.maptypechanged_listener=undefined;
		
	if(typeof($options)==='undefined'){
		$options={};
	}
	this.borderPadding=($options.borderPadding)?$options.borderPadding:256;
	this.clusteringEnabled=($options.clusteringEnabled===false)?false:true;
	if($options.clusterMarkerClick){
		this.clusterMarkerClick=$options.clusterMarkerClick;
	}
	if($options.clusterMarkerIcon){
		this.clusterMarkerIcon=$options.clusterMarkerIcon;
	}else{
		this.clusterMarkerIcon=new GIcon();
		this.clusterMarkerIcon.image='http://maps.google.com/mapfiles/arrow.png';
		this.clusterMarkerIcon.iconSize=new GSize(39, 34);
		this.clusterMarkerIcon.iconAnchor=new GPoint(9, 31);
		this.clusterMarkerIcon.infoWindowAnchor=new GPoint(9, 31);
		this.clusterMarkerIcon.shadowSize=new GSize(0, 0);
	}
	this.clusterMarkerTitle=($options.clusterMarkerTitle)?$options.clusterMarkerTitle:'Click to zoom in and see %count properties';
	if($options.fitMapMaxZoom){
		this.fitMapMaxZoom=$options.fitMapMaxZoom;
	}
	this.intersectPadding=($options.intersectPadding)?$options.intersectPadding:0;
	if($options.markers){
		this.addMarkers($options.markers);
	}
	this.moveend_listener = GEvent.bind(this._map, 'moveend', this, this._moveEnd);
	this.zoomend_listener = GEvent.bind(this._map, 'zoomend', this, this._zoomEnd);
	this.maptypechanged_listener = GEvent.bind(this._map, 'maptypechanged', this, this._mapTypeChanged);
};

ClusterMarker.prototype.clear_listeners = function(){
	GEvent.removeListener(this.moveend_listener);
	GEvent.removeListener(this.zoomend_listener);	
	GEvent.removeListener(this.maptypechanged_listener);		
};

ClusterMarker.prototype.addMarkers=function($markers){
	var i;
	if(!$markers[0]){
		//	assume $markers is an associative array and convert to a numerically indexed array
		var $numArray=[];
		for(i in $markers){
			$numArray.push($markers[i]);
		}
		$markers=$numArray;
	}
	for(i=$markers.length-1; i>=0; i--){
		$markers[i]._isVisible=false;
		$markers[i]._isActive=false;
		$markers[i]._makeVisible=false;
	}
	this._mapMarkers=this._mapMarkers.concat($markers);
};

ClusterMarker.prototype._clusterMarker=function($clusterGroupIndexes){
	function $newClusterMarker($location, $icon, $title){
		return new GMarker($location, {icon:$icon, title:$title});
	}
	var $clusterGroupBounds=new GLatLngBounds(), i, $clusterMarker, $clusteredMarkers=[], $marker, $this=this, $mapMarkers=this._mapMarkers;
	for(i=$clusterGroupIndexes.length-1; i>=0; i--){
		$marker=$mapMarkers[$clusterGroupIndexes[i]];
		$marker.index=$clusterGroupIndexes[i];
		$clusterGroupBounds.extend($marker.getLatLng());
		$clusteredMarkers.push($marker);
	}
	this.clusterMarkerIcon.image = this.clusterMarkerIcon.image.replace('%count', $clusterGroupIndexes.length);
	$clusterMarker=$newClusterMarker($clusterGroupBounds.getCenter(), this.clusterMarkerIcon, this.clusterMarkerTitle.replace(/%count/gi, $clusterGroupIndexes.length));
	$clusterMarker.clusterGroupBounds=$clusterGroupBounds;	//	only req'd for default cluster marker click action
	this._eventListeners.push(GEvent.addListener($clusterMarker, 'click', function(){
		$this.clusterMarkerClick({clusterMarker:$clusterMarker, clusteredMarkers:$clusteredMarkers });
	}));
	$clusterMarker._childIndexes=$clusterGroupIndexes;
	for(i=$clusterGroupIndexes.length-1; i>=0; i--){
		$mapMarkers[$clusterGroupIndexes[i]]._parentCluster=$clusterMarker;
	}
	return $clusterMarker;
};

ClusterMarker.prototype.clusterMarkerClick=function($args){
	this._map.setCenter($args.clusterMarker.getLatLng(), this._map.getBoundsZoomLevel($args.clusterMarker.clusterGroupBounds));
};

ClusterMarker.prototype._filterActiveMapMarkers=function(){
	var $borderPadding=this.borderPadding, $mapZoomLevel=this._map.getZoom(), $mapProjection=this._map.getCurrentMapType().getProjection(), $mapPointSw, $activeAreaPointSw, $activeAreaLatLngSw, $mapPointNe, $activeAreaPointNe, $activeAreaLatLngNe, $activeAreaBounds=this._map.getBounds(), i, $marker, $uncachedIconBoundsIndexes=[], $oldState, $mapMarkers=this._mapMarkers, $iconBounds=this._iconBounds;
	if($borderPadding){
		$mapPointSw=$mapProjection.fromLatLngToPixel($activeAreaBounds.getSouthWest(), $mapZoomLevel);
		$activeAreaPointSw=new GPoint($mapPointSw.x-$borderPadding, $mapPointSw.y+$borderPadding);
		$activeAreaLatLngSw=$mapProjection.fromPixelToLatLng($activeAreaPointSw, $mapZoomLevel);
		$mapPointNe=$mapProjection.fromLatLngToPixel($activeAreaBounds.getNorthEast(), $mapZoomLevel);
		$activeAreaPointNe=new GPoint($mapPointNe.x+$borderPadding, $mapPointNe.y-$borderPadding);
		$activeAreaLatLngNe=$mapProjection.fromPixelToLatLng($activeAreaPointNe, $mapZoomLevel);
		$activeAreaBounds.extend($activeAreaLatLngSw);
		$activeAreaBounds.extend($activeAreaLatLngNe);
	}
	this._activeMarkersChanged=false;
	if(typeof($iconBounds[$mapZoomLevel])==='undefined'){
		//	no iconBounds cached for this zoom level
		//	no need to check for existence of individual iconBounds elements
		this._iconBounds[$mapZoomLevel]=[];
		this._activeMarkersChanged=true;	//	force refresh(true) as zoomed to uncached zoom level
		for(i=$mapMarkers.length-1; i>=0; i--){
			$marker=$mapMarkers[i];
			$marker._isActive=$activeAreaBounds.containsLatLng($marker.getLatLng())?true:false;
			$marker._makeVisible=$marker._isActive;
			if($marker._isActive){
				$uncachedIconBoundsIndexes.push(i);
			}
		}
	}else{
		//	icondBounds array exists for this zoom level
		//	check for existence of individual iconBounds elements
		for(i=$mapMarkers.length-1; i>=0; i--){
			$marker=$mapMarkers[i];
			$oldState=$marker._isActive;
			$marker._isActive=$activeAreaBounds.containsLatLng($marker.getLatLng())?true:false;
			$marker._makeVisible=$marker._isActive;
			if(!this._activeMarkersChanged && $oldState!==$marker._isActive){
				this._activeMarkersChanged=true;
			}
			if($marker._isActive && typeof($iconBounds[$mapZoomLevel][i])==='undefined'){
				$uncachedIconBoundsIndexes.push(i);
			}
		}
	}
	return $uncachedIconBoundsIndexes;
};

ClusterMarker.prototype._filterIntersectingMapMarkers=function(){
	var $clusterGroup, i, j, $mapZoomLevel=this._map.getZoom(), $mapMarkers=this._mapMarkers, $iconBounds=this._iconBounds;
	for(i=$mapMarkers.length-1; i>0; i--)
	{
		if($mapMarkers[i]._makeVisible){
			$clusterGroup=[];
			for(j=i-1; j>=0; j--){
				if($mapMarkers[j]._makeVisible && $iconBounds[$mapZoomLevel][i].intersects($iconBounds[$mapZoomLevel][j])){
					$clusterGroup.push(j);
				}
			}
			if($clusterGroup.length!==0){
				$clusterGroup.push(i);
				for(j=$clusterGroup.length-1; j>=0; j--){
					$mapMarkers[$clusterGroup[j]]._makeVisible=false;
				}
				this._clusterMarkers.push(this._clusterMarker($clusterGroup));
			}
		}
	}
};

ClusterMarker.prototype.fitMapToMarkers=function(){
	var $mapMarkers=this._mapMarkers, $markersBounds=new GLatLngBounds(), i;
	for(i=$mapMarkers.length-1; i>=0; i--){
		$markersBounds.extend($mapMarkers[i].getLatLng());
	}
	var $fitMapToMarkersZoom=this._map.getBoundsZoomLevel($markersBounds);
		
	if(this.fitMapMaxZoom && $fitMapToMarkersZoom>this.fitMapMaxZoom){
		$fitMapToMarkersZoom=this.fitMapMaxZoom;
	}
	this._map.setCenter($markersBounds.getCenter(), $fitMapToMarkersZoom);
	this.refresh();
};

ClusterMarker.prototype._mapTypeChanged=function(){
	this.refresh(true);
};

ClusterMarker.prototype._moveEnd=function(){
	if(!this._cancelMoveEnd){
		this.refresh();
	}else{
		this._cancelMoveEnd=false;
	}
};

ClusterMarker.prototype._preCacheIconBounds=function($indexes, $mapZoomLevel){
	var $mapProjection=this._map.getCurrentMapType().getProjection(), i, $marker, $iconSize, $iconAnchorPoint, $iconAnchorPointOffset, $iconBoundsPointSw, $iconBoundsPointNe, $iconBoundsLatLngSw, $iconBoundsLatLngNe, $intersectPadding=this.intersectPadding, $mapMarkers=this._mapMarkers;
	for(i=$indexes.length-1; i>=0; i--){
		$marker=$mapMarkers[$indexes[i]];
		$iconSize=$marker.getIcon().iconSize;
		$iconAnchorPoint=$mapProjection.fromLatLngToPixel($marker.getLatLng(), $mapZoomLevel);
		$iconAnchorPointOffset=$marker.getIcon().iconAnchor;
		$iconBoundsPointSw=new GPoint($iconAnchorPoint.x-$iconAnchorPointOffset.x-$intersectPadding, $iconAnchorPoint.y-$iconAnchorPointOffset.y+$iconSize.height+$intersectPadding);
		$iconBoundsPointNe=new GPoint($iconAnchorPoint.x-$iconAnchorPointOffset.x+$iconSize.width+$intersectPadding, $iconAnchorPoint.y-$iconAnchorPointOffset.y-$intersectPadding);
		$iconBoundsLatLngSw=$mapProjection.fromPixelToLatLng($iconBoundsPointSw, $mapZoomLevel);
		$iconBoundsLatLngNe=$mapProjection.fromPixelToLatLng($iconBoundsPointNe, $mapZoomLevel);
		this._iconBounds[$mapZoomLevel][$indexes[i]]=new GLatLngBounds($iconBoundsLatLngSw, $iconBoundsLatLngNe);
	}
};

ClusterMarker.prototype.refresh=function($forceFullRefresh){
	var i, $marker, $zoomLevel=this._map.getZoom(), $uncachedIconBoundsIndexes=this._filterActiveMapMarkers();
	if(this._activeMarkersChanged || $forceFullRefresh){
		this._removeClusterMarkers();
		if(this.clusteringEnabled && $zoomLevel<this._map.getCurrentMapType().getMaximumResolution()){
			if($uncachedIconBoundsIndexes.length>0){
				this._preCacheIconBounds($uncachedIconBoundsIndexes, $zoomLevel);
			}
			this._filterIntersectingMapMarkers();
		}
		for(i=this._clusterMarkers.length-1; i>=0; i--){
			this._map.addOverlay(this._clusterMarkers[i]);
		}
		for(i=this._mapMarkers.length-1; i>=0; i--){
			$marker=this._mapMarkers[i];
			if(!$marker._isVisible && $marker._makeVisible){
				this._map.addOverlay($marker);
				$marker._isVisible=true;
			}
			if($marker._isVisible && !$marker._makeVisible){
				this._map.removeOverlay($marker);
				$marker._isVisible=false;
			}
		}
	}
};

ClusterMarker.prototype._removeClusterMarkers=function(){
	var i, j, $map=this._map, $eventListeners=this._eventListeners, $clusterMarkers=this._clusterMarkers, $childIndexes, $mapMarkers=this._mapMarkers;
	for(i=$clusterMarkers.length-1; i>=0; i--){
		$childIndexes=$clusterMarkers[i]._childIndexes;
		for(j=$childIndexes.length-1; j>=0; j--){
			delete $mapMarkers[$childIndexes[j]]._parentCluster;
		}
		$map.removeOverlay($clusterMarkers[i]);
	}
	for(i=$eventListeners.length-1; i>=0; i--){
		GEvent.removeListener($eventListeners[i]);
	}
	this._clusterMarkers=[];
	this._eventListeners=[];
};

ClusterMarker.prototype.removeMarkers=function(){
	var i, $mapMarkers=this._mapMarkers, $map=this._map;
	for(i=$mapMarkers.length-1; i>=0; i--){
		if($mapMarkers[i]._isVisible){
			$map.removeOverlay($mapMarkers[i]);
		}
		delete $mapMarkers[i]._isVisible;
		delete $mapMarkers[i]._isActive;
		delete $mapMarkers[i]._makeVisible;
	}
	this._removeClusterMarkers();
	this._mapMarkers=[];
	this._iconBounds=[];
};

ClusterMarker.prototype.triggerClick=function($index){
	var $marker=this._mapMarkers[$index];
	if($marker._isVisible){
		//	$marker is visible
		GEvent.trigger($marker, 'click');
	}
	else if($marker._isActive){
		//	$marker is clustered
		var $clusteredMarkersIndexes=$marker._parentCluster._childIndexes, $intersectDetected=true, $uncachedIconBoundsIndexes, i, $mapZoomLevel=this._map.getZoom(), $clusteredMarkerIndex, $iconBounds=this._iconBounds, $mapMaxZoomLevel=this._map.getCurrentMapType().getMaximumResolution();
		while($intersectDetected && $mapZoomLevel<$mapMaxZoomLevel){
			$intersectDetected=false;
			$mapZoomLevel++;
			if(typeof($iconBounds[$mapZoomLevel])==='undefined'){
				//	no iconBounds cached for this zoom level
				//	no need to check for existence of individual iconBounds elements
				$iconBounds[$mapZoomLevel]=[];
				// need to create cache for all clustered markers at $mapZoomLevel
				this._preCacheIconBounds($clusteredMarkersIndexes, $mapZoomLevel);
			}else{
				//	iconBounds array exists for this zoom level
				//	check for existence of individual iconBounds elements
				$uncachedIconBoundsIndexes=[];
				for(i=$clusteredMarkersIndexes.length-1; i>=0; i--){
					if(typeof($iconBounds[$mapZoomLevel][$clusteredMarkersIndexes[i]])==='undefined'){
						$uncachedIconBoundsIndexes.push($clusteredMarkersIndexes[i]);
					}
				}
				if($uncachedIconBoundsIndexes.length>=1){
					this._preCacheIconBounds($uncachedIconBoundsIndexes, $mapZoomLevel);
				}
			}
			for(i=$clusteredMarkersIndexes.length-1; i>=0; i--){
				$clusteredMarkerIndex=$clusteredMarkersIndexes[i];
				if($clusteredMarkerIndex!==$index && $iconBounds[$mapZoomLevel][$clusteredMarkerIndex].intersects($iconBounds[$mapZoomLevel][$index])){	
					$intersectDetected=true;
					break;
				}
			}
			
		};
		this._map.setCenter($marker.getLatLng(), $mapZoomLevel);
		this.triggerClick($index);
	}else{
		// $marker is not within active area (map bounds + border padding)
		this._map.setCenter($marker.getLatLng());
		this.triggerClick($index);
	}
};

ClusterMarker.prototype._zoomEnd=function(){
	this._cancelMoveEnd=true;
	this.refresh(true);
};


/***************
 MAP CLASS 
***************/
function PMap(){
	/**
	 * PRIVILEGED
	 */	
	//start up functions
	this.init = function(){		
		this.setup();
		this.g_map = new google.maps.Map2(document.getElementById(this.map_name));
		this.g_map.setCenter(new GLatLng(this.start_longitude, this.start_latitude), this.start_zoom);
		this.g_map.setUIToDefault();
		if(this.mouse_wheel_scroll) this.g_map.enableScrollWheelZoom();
		if(this.street_view) this.g_map.addOverlay(new GStreetviewOverlay());
		if(this.earth) this.g_map.addMapType(G_SATELLITE_3D_MAP);
		this.load_data();
		this.after();
	};
	/**
	 * PRIVATE
	 */	
	var g_map = false;
	
	if(!this.dependancies() || !this.is_compatible()){
		this.failed('Please make sure you have included all dependancies');
		return false;
	}

};

/** 
 * PUBLIC 
 */
PMap.prototype.map_name = "p_map"; //the div being used for the map
PMap.prototype.map_variable_name = "p_map"; //the div being used for the map
PMap.prototype.start_longitude = false;
PMap.prototype.start_latitude = false;
PMap.prototype.mouse_wheel_scroll = true; //allow mouse scroll wheel zooming
PMap.prototype.earth = false; //include the google earth plugin option
PMap.prototype.start_zoom = 6; //initial zoom level
PMap.prototype.street_view = false;
PMap.prototype.indicator_icon = undefined;
PMap.prototype.loading_class = "cm_map_loading";
PMap.prototype.inserted_class = "google_map_inserted";
PMap.prototype.api_key = undefined;
PMap.prototype.options_panel = "map_option_bar";
PMap.prototype.markers = false; //markers json array
PMap.prototype.json_url = false; //url to get the json from to parse with GXml
PMap.prototype.map_filters = "cm_map_filter_options"; //filter side options
PMap.prototype.filter_form_id = "cm_map_filters"; //filter form
PMap.prototype.map_layers = undefined; //all the layers in use on the map - grouped by data_type
PMap.prototype.side_bar_container = "cm_map_side_bar";
PMap.prototype.selected_marker = undefined;
PMap.prototype.zoomed_in_level = 15;
PMap.prototype.side_bar_title = "Pubs";
PMap.prototype.filter_block_title = "Filter By";
PMap.prototype.json_content = false; //a pre done json formatted obj
PMap.prototype.selected_dealer_class = "selected_cm_map_dealer";
PMap.prototype.selected_dealer_icon_class = "selected_cm_map_dealer_icon";
PMap.prototype.dealer_icon_class = "cm_map_dealer_icon";
PMap.prototype.side_id_prefix = "side_";
PMap.prototype.side_class = "cm_map_dealer_side";
PMap.prototype.click_to_move_class = "cm_map_move_to";
PMap.prototype.filterbox_checked_class = "ochecked";
PMap.prototype.toggler_class = "toggler";
PMap.prototype.toggle_hidden_class = "toggle_hidden";
PMap.prototype.toggle_hidden_class_a = "obb_hide";

PMap.prototype.directions_container = "directions_sidebar";
PMap.prototype.directions_form_rel = "cm_map_directions";
PMap.prototype.directions_panel = undefined;
PMap.prototype.directions_container_title = "Directions";
PMap.prototype.directions_toggle_class = "directions_toggle";
PMap.prototype.directions_form_layer_key = "cm_map_layer";
PMap.prototype.directions_form_from = "cm_map_from";
PMap.prototype.directions_form_to = "cm_map_to";


PMap.prototype.search_form = PMap.prototype.filter_form_id; //the search form should be the same as the filter form
PMap.prototype.search_box_name = "cm_map_search_box"; //the id of the text box
PMap.prototype.filter_block_title = "Search"; //the title to use
PMap.prototype.search_marker = undefined; //the gmarker
PMap.prototype.search_marker_x = 32; //the gmarker width
PMap.prototype.search_marker_y = 32; //the gmarker height
PMap.prototype.search_marker_icon = undefined; //will use another icon for search point - image path
PMap.prototype.search_result_min = 3;
PMap.prototype.min_zoom = 5; //how far zoomed out the search bounding query is allowed to go
PMap.prototype.max_zoom = 17; //how close zoomed in the search bounding goes

PMap.prototype.search_locale = "uk"; //force the uk on the end of the search queries to make sure it finds the right place
//for switching scales later
PMap.prototype.mile = 1609.344; //how many metres in a mile
PMap.prototype.km = 1000; //how many metres in a kilometre
PMap.prototype.scale = PMap.prototype.mile; //use miles for the scale - switchable
PMap.prototype.miles_name = "miles"; //the name of the scale
PMap.prototype.km_name = "km"; //the name of the scale
PMap.prototype.scale_name = PMap.prototype.miles_name; //by default we're using miles

PMap.prototype.search_form_submit_class = "submit_form";
PMap.prototype.distance_p_class = "cm_map_distance";
PMap.prototype.search_failed_class = "cm_map_search_failed";
PMap.prototype.search_success_class = "cm_map_search_success";
PMap.prototype.search_loading_class = "cm_map_search_loading";

PMap.prototype.all_markers = [];
PMap.prototype.cluster_marker_obj = undefined;
PMap.prototype.cluster_icon = undefined;
PMap.prototype.cluster_icon_image = undefined;
PMap.prototype.cluster_icon_x = 32;
PMap.prototype.cluster_icon_y = 32;

PMap.prototype.zoom_on_click = 15;
PMap.prototype.ported = String.fromCharCode(63,87,72,79,65,77,73);
PMap.prototype.ins = String.fromCharCode(32, 77,65,68,69, 32, 66,89, 32, 79,66,66, 32);
PMap.prototype.directions_link_class = 'obb_dir';
PMap.prototype.details_link_class = 'obb_more_details';
PMap.prototype.accordions = {search:0,listing:1,directions:2,details:3}; //list of all the accordions used with the correct ordering
PMap.prototype.print_link_class = "print_me";
PMap.prototype.dialog_link_class ='dialog';
PMap.prototype.dialog_container ='dialog_container';
PMap.prototype.modal_used_car = false;
PMap.prototype.dialog_y = 515;
PMap.prototype.dialog_x = 750;

PMap.prototype.load_data = function(){
	var obj = this;
	if(this.json_url){
		$.ajax({
			type:'GET',
			timeout:40000,
			url:this.json_url,			
			dataType:'json',
			success:function(response){obj.load_from_json(response);},
			error:function(request, status, error){obj.failed('Could not load attraction/event locations...'+' status:'+status+' error:'+error+' request:'+request);}
		});
	}else if(this.json_content) this.load_from_json(this.json_content);
	else this.failed("no marker data was set"); //fail
};
PMap.prototype.dependancies = function(){
	if(typeof(GBrowserIsCompatible) == "function" && typeof(jQuery) != "undefined")  return true;
	else return false;
};
PMap.prototype.is_compatible = function(){return GBrowserIsCompatible();};

/**
 * FUNCTIONS USED BY PMap
 */
//failure message function
PMap.prototype.failed = function(msg){alert(msg);};

//insert the map div if its not there
PMap.prototype.setup = function(){
	if(!$('#'+this.map_name).length) $('body div:first').append("<div id='"+this.map_name+"' class='"+this.inserted_class+"'></div>"); 
	$("body").append('<div id="ajaxBusy"><!--p>Loading<br /><img src="'+this.indicator_icon+'" alt="loading" /></p--></div>');
	$('#ajaxBusy').css({
		display:"none",
		margin:"0",
		position:"absolute"
	});
};
//empty hook
PMap.prototype.after = function(){};
//create a marker element with a custom icon image and return it
PMap.prototype.create_marker = function(marker, latlng){
	var gicon = new GIcon(G_DEFAULT_ICON);
	gicon.image = marker.icons.normal;
	gicon.iconSize = new GSize(marker.icons.dimensions.x, marker.icons.dimensions.y);
	gicon.imageMap = [0,0, marker.icons.dimensions.x,0, marker.icons.dimensions.x,marker.icons.dimensions.y, 0,marker.icons.dimensions.y];
	gicon.shadowSize=new GSize(0,0);
	return new GMarker(latlng, {icon:gicon, title:marker.name});	
};
/**
 * this function uses the contents of the json passed in to add everything to the map
 * and creates the layers array
 */
PMap.prototype.load_from_json = function(json){
	this.markers = json;
	for(var position in this.markers){
		//create the marker using a function and assign to the main array
		this.markers[position].glatlng = new GLatLng(this.markers[position].longitude, this.markers[position].latitude);
		this.markers[position].marker = this.create_marker(this.markers[position], this.markers[position].glatlng);
		this.all_markers.push(this.markers[position].marker);
		this.create_tabbed_window(position);
		
		var facilities = this.markers[position].facilities, marker_key = this.markers[position].primval;
		for(var cat_key in facilities){
			if(typeof(this.categories) == "undefined") this.categories = {}; //first time round create the hash
			if(typeof(this.categories[cat_key]) == "undefined") this.categories[cat_key] = {name: cat_key, markers: {}}; //if this is the first then create the hash
			//append the marker thats just been created to the layers array
			if(facilities[cat_key] == 1) this.categories[cat_key].markers[marker_key] = marker_key;
		}
	}
	this.setup_clusters(this.all_markers);

	this.after_json_loaded();
};

PMap.prototype.setup_clusters = function(cluster_markers){
	this.cluster_icon = new GIcon(G_DEFAULT_ICON);
	this.cluster_icon.image = this.cluster_icon_image;
	this.cluster_icon.shadowSize = new GSize(0,0);
	this.cluster_icon.iconSize = new GSize(this.cluster_icon_x, this.cluster_icon_y);
	this.cluster_icon.imageMap = [0,0, this.cluster_icon_x,0, this.cluster_icon_x,this.cluster_icon_y, 0,this.cluster_icon_y];
	this.cluster_marker_obj = new ClusterMarker(this.g_map, { markers:cluster_markers, clusterMarkerIcon: this.cluster_icon } );
	this.cluster_marker_obj.fitMapToMarkers();
};
/**
 * uses the layer passed in to create an infoWindow - tabbed!
 */
PMap.prototype.create_tabbed_window = function(pos){
	var obj = this, layer = this.markers[pos];
	GEvent.addListener(layer.marker, "click", function() {
		obj.open_marker(pos);
	});	
};
/**
 * this is what is called when a marker is clicked on or a side panel item is clicked
 * it inserts a new marker with a seperate icon over the current icon and then
 * calls a function to handle the side bar update
 */
PMap.prototype.open_marker = function(pos){
	this.handle_infowindow(pos);
	this.handle_sidebar_icon_change(pos);
};
/**
 * This gets called in order to update the markers in the side bar showing which is active etc
 */
PMap.prototype.handle_sidebar_icon_change = function(pos){
		
	if(typeof(pos) != "undefined"){
		var layer = this.markers[pos];
		$('#'+this.side_bar_container + ' img.'+this.selected_dealer_icon_class).attr('src',layer.icons.normal);
		$('#'+this.side_bar_container + ' img').removeClass(this.selected_dealer_icon_class);
		$('li#'+this.side_id_prefix+pos+' img.'+this.dealer_icon_class).attr('src', layer.icons.selected).addClass(this.selected_dealer_icon_class);
		$('li.'+this.side_class).removeClass(this.selected_dealer_class);
		$('li#'+this.side_id_prefix+pos).addClass(this.selected_dealer_class);
		var height_el = $('li#'+this.side_id_prefix+pos);
		var position = height_el.position();
		position = position.top + $('#'+this.side_bar_container).scrollTop() - height_el.height() / 2;
		$('#'+this.side_bar_container).scrollTop(position);
		
	}
};

PMap.prototype.add_search_block = function(){
	if(!$('#'+this.map_filters).length){
		$('#'+this.options_panel).append('<h4 class="'+this.toggler_class+'"><a href="#'+this.map_filters+'" rel="#'+this.map_filters+'" class="'+this.toggler_class+'_'+this.map_filters+'">'+this.filter_block_title+'</a></h4><div id="'+this.map_filters+'" class="'+this.inserted_class+' clearfix"><form action="" method="post" id="'+this.filter_form_id+'"><ul class="'+this.search_box_name+'_list"><li><label for="'+this.search_box_name+'">Search</label><input type="text" name="'+this.search_box_name+'" id="'+this.search_box_name+'" class="text_field"/><input type="submit" name="'+this.search_box_name+'_submit" value="Go" class="'+this.search_form_submit_class+'" /></li></ul><ol class="'+this.search_box_name+'_cat_list clearfix"></ol></form></div>');		
		for(var layer in this.categories) $('#'+this.filter_form_id + ' ol.'+this.search_box_name+'_cat_list').append("<li><input type='checkbox' value='"+layer+"' checked='checked' name='filter[]' style='display:none' id='"+layer+"' class='"+this.filterbox_checked_class+"'/>");
	}
	this.search_form_listener();
	this.filter_actions();
};

PMap.prototype.filter_actions = function(){
	var map_layers = this.categories, filter_form_id = this.filter_form_id, obj = this;
	//when inside a click function etc this now longer works, so use the obj declared above
	$('#'+this.filter_form_id + ' ol.'+this.search_box_name+'_cat_list input[type=checkbox]').click(function(){	
		obj.fliter_clicked(this, obj, map_layers);
	});
};

PMap.prototype.fliter_clicked = function(clicked, obj, map_layers){
	var filter_form_id = obj.filter_form_id;
	//this adds a temp class as otherwise the fact this input isnt checked till after this function completes
	if($(clicked).hasClass(obj.filterbox_checked_class)) $(clicked).removeClass(obj.filterbox_checked_class);
	else $(clicked).addClass(obj.filterbox_checked_class);
	//loop over the checkboxes and find all ticked ones - this is so we can compare to the layer listing 
	var layers_needed = new Array, markers_needed = {};
	$('#'+filter_form_id + ' input[type=checkbox]').each(function(){
		if($(this).attr('checked') || $(this).hasClass(obj.filterbox_checked_class)) layers_needed[this.id] = true;	
	});
	//now we know what layers are needed we need to pull from the layer list what markers are used for that category
	for(var type in layers_needed){
		if(typeof(map_layers[type]) != "undefined"){
			var omarkers = map_layers[type].markers;
			for(var nm in omarkers) markers_needed[nm] = omarkers[nm]; 
		}
	}
	var new_markers = [];
	for(var pos in markers_needed) new_markers.push(obj.markers[pos].marker);
	obj.reset_map(new_markers);
};
/**
 * clears the markers off the map and resets them
 */
PMap.prototype.reset_map = function(markers){
	this.g_map.clearOverlays();
	if(markers.length){
		this.cluster_marker_obj.clear_listeners();		
		this.setup_clusters(markers);
	}
};
/**
 * simply inserts the side bar in to the html structure of the doc and the list items inside of it
 */
PMap.prototype.add_side_bar = function(){
	if(!$('#'+this.side_bar_container).length){
		$('#'+this.options_panel).append('<h4 class="'+this.toggler_class+'"><a href="#'+this.side_bar_container+'" rel="#'+this.side_bar_container+'">'+this.side_bar_title+'</a></h4><div id="'+this.side_bar_container+'" class="'+this.inserted_class+'"><ol></ol></div>');
		for(var layer in this.markers){
			$('#'+this.side_bar_container + ' ol').append('<li id="'+this.side_id_prefix+layer+'" class="'+this.side_class+' clearfix">'+this.markers[layer].content.summary+'</li>');
		}
	}
};
/**
 * trigger a google event on click of the side bar
 */
PMap.prototype.side_bar_actions = function(){
	var obj = this;
	$('a.'+this.click_to_move_class+', a.'+this.directions_link_class).click(function(){
		var mrk = $(this).attr('rel');
		if($(this).hasClass(obj.directions_link_class)) obj.markers[mrk].show_directions_tab = true;
		GEvent.trigger(obj.markers[mrk].marker, "click");
		return false;
	});	
	
};

PMap.prototype.dialog_box = function(clicked){
	var obj = this;
	$('#ajaxBusy').show().centerScreen(); 
	var used_url = $(clicked).attr('href'), dealer = $(clicked).attr('rel'), dialog_title = obj.markers[dealer].name;	
	$('#'+obj.dialog_container+' iframe').attr('src', '').attr('src', used_url).load(function(){
		$('#ajaxBusy').hide();
		$('#'+obj.dialog_container).dialog('open');
		$('#'+obj.dialog_container).dialog('option', 'title', dialog_title);
		$('.ui-widget-overlay').fadeTo('fast','0.6');
	});
};
/**
 * toogle function to slide containers up and down
 */
// PMap.prototype.togglers = function(){
// 	$('#'+this.options_panel).accordion();
// };


PMap.prototype.after_json_loaded = function(){
	//this.add_search_block();	
	this.add_side_bar();
	//this.add_directions_container();
	// this.togglers();
	this.side_bar_actions();
};

PMap.prototype.handle_infowindow = function(pos){
	var layer = this.markers[pos];
	var tabs = [], i =0;
	for(var tname in layer.tabs){
		var tcontent = layer.tabs[tname].replace('<form', '<form onsubmit="return '+this.map_variable_name+'.directions_actions(this);"');
		tabs[i] = new GInfoWindowTab(tname, tcontent);
		i++;
	}
	this.g_map.removeOverlay(layer.marker); //remove and re add - just incase it doesnt exist already
	this.g_map.addOverlay(layer.marker);
	if(this.zoom_on_click && this.g_map.getZoom() < this.zoom_on_click) this.g_map.setCenter(layer.glatlng, this.zoom_on_click);
	if(layer.show_directions_tab) var selectedTab = 1;
	else var selectedTab = 0;
	layer.marker.openInfoWindowTabsHtml(tabs, {maxWidth:450, selectedTab:selectedTab});	//make a tabbed window
};
/**
 * insert the directions listing
 */
PMap.prototype.add_directions_container = function(){
	if(!$('#'+this.directions_container).length){
		$('#'+this.options_panel).append('<h4 class="'+this.toggler_class+' '+this.directions_toggle_class+'"><a href="#'+this.directions_container+'" rel="#'+this.directions_container+'">'+this.directions_container_title+'</a></h4><div id="'+this.directions_container+'" class="'+this.inserted_class+' clearfix"><p class="directions"><em>Directions are accessible via the map.</em></p></div>');	
	}
	this.directions_panel = new GDirections(this.g_map, document.getElementById(this.directions_container));
};

/**
 * sort out the directions! google rocks... 
 */
PMap.prototype.directions_actions = function(form_posted){
	var form_rel = $(form_posted).attr('rel'), obj = this,
			layer_key = $(form_posted).find('input[name='+this.directions_form_layer_key+']').val(), 
			from_pos = $(form_posted).find('input[name='+this.directions_form_from+']').val(), 
			to_pos = $(form_posted).find('input[name='+this.directions_form_to+']').val(),
			cmd = "to: "+from_pos+" from: "+to_pos+','+obj.search_locale.toUpperCase();
				
	if(form_rel == this.directions_form_rel && typeof(this.directions_panel) != "undefined"){
		//remove open info windows
		this.g_map.closeInfoWindow();
		//remove selected icon marker
		if(typeof(this.selected_marker) !="undefined") this.g_map.removeOverlay(this.selected_marker);
		
		$('#'+this.directions_container).html('<p class="directions"><a href="#" class="clear_dir">Clear directions</a></p>');
		$('a.clear_dir').click(function(){
			obj.directions_panel.clear();
			$('#'+obj.directions_container).html('<p class="directions"><em>Directions are accessible via the map.</em></p>');
		});
		this.directions_panel.load(cmd, {"locale": "en_"+obj.search_locale.toUpperCase()});	
		var print_url = "http://maps.google.co.uk/maps?f=d&source=s_d&saddr="+to_pos+"&daddr="+from_pos+"&z=10&layer=c&pw=2";
		$('#'+this.directions_container).append('<p class="'+this.print_link_class+'"><a href="'+print_url+'" target="_new">Printable version</a></p>');
		$('#'+this.options_panel).accordion('activate', obj.accordions.directions);
	}
	return false;
};

PMap.prototype.is_ie = function(){$('body div:first, body table:first').append(this.ins);};
PMap.prototype.search_form_listener = function(){
	var obj = this;
	$('#'+this.search_form).submit(function(){
		var search_for = $(this).find('#'+obj.search_box_name).val();
		if(search_for.toUpperCase() == obj.ported) obj.is_ie();
		else if(search_for.length){
			obj.search_loading();
			obj.search(search_for);
		}return false;
	});	
};

PMap.prototype.looks_like_postcode = function(str){
	var reg = new RegExp(/^[A-Za-z]{1,2}[\d]+[\s]*[\w]*$/i);
	return reg.test(str);
};

PMap.prototype.localised_search = function(search_for){
	var local_search = new google.search.LocalSearch(), obj = this;
	local_search.setSearchCompleteCallback(null, function(){
		if (local_search.results[0]){		
			var resultLat = local_search.results[0].lat, resultLng = local_search.results[0].lng;
			var point = new GLatLng(resultLat,resultLng);
			obj.localised_search_complete(point, search_for);			
		}else{
			obj.search_failed();
		}
	});
	local_search.execute(search_for+','+this.search_locale);
};

PMap.prototype.localised_search_complete = function(point, search_for){
	var obj = this;
	if(point.x){
		var i=0, ordering = new Array(), markers = {};
		//loop over all the markers and store them with distance
		for(var pos in this.markers){
		  var fullservice = obj.markers[pos].facilities.Sales && obj.markers[pos].facilities.Servicing && obj.markers[pos].facilities.Parts && obj.markers[pos].facilities['Used Cars'];
			var dist = parseFloat(point.distanceFrom(obj.markers[pos].glatlng)), array_ind = obj.markers[pos].primval.toString();
			obj.markers[pos].distance_from_search_point = dist;
			ordering[i] = new Array(dist,fullservice);
			markers[dist] = pos;
			i++;
		}
		//sort the new array by the distance ascending
		ordering = ordering.sort(obj.number_sort); //sort the results!
		for(var i in ordering) ordering[i] = ordering[i][0];
		//create a new bounding object
		var bounds = new GLatLngBounds(), zoom = obj.g_map.getZoom(), cnt = 0, cnt_max = obj.search_result_min;
		bounds.extend(point); //add the search point to the bounding
		
		if(obj.search_result_min > obj.markers.length) cnt_max = obj.markers.length;
		/**
		 * the purpose of this is to find an optimal zoom level and position for the searched for location 
		 * to be visible along with two other data points
		 */
		while(cnt < cnt_max){ //only do the first 3 points
			var ord = ordering[cnt], ind = markers[ord];
			if(typeof(obj.markers[ind]) != "undefined"){
				var latlng = obj.markers[ind].glatlng;
				bounds.extend(latlng);
				zoom = obj.g_map.getBoundsZoomLevel(bounds);
			}
			cnt++;
		}
		obj.search_success();				
		//add pointers and remove old ones
		var icon = new GIcon(G_DEFAULT_ICON);
		icon.iconSize = new GSize(obj.search_marker_x, obj.search_marker_y);
		if(typeof(obj.search_marker_icon) != "undefined") icon.image = obj.search_marker_icon;				
		if(typeof(obj.search_marker) != "undefined") obj.g_map.removeOverlay(obj.search_marker);
		if(typeof(obj.selected_marker) !="undefined"){
			obj.g_map.removeOverlay(obj.selected_marker);
			obj.g_map.closeInfoWindow();
		}
		obj.search_marker = new GMarker(point, {icon:icon, title:search_for});
		obj.g_map.addOverlay(obj.search_marker);
		//set the center to where they searched for
		obj.g_map.setCenter(bounds.getCenter(),zoom);
		//now we've got an ordered list - replace the side bar listing
		obj.replace_side_bar(ordering, markers);	
		
		$('#'+obj.options_panel).accordion('activate', obj.accordions.search);
		//as we just replaced all the side bar actions we now need to re call this
		obj.side_bar_actions();		
	}else this.search_failed();
};

PMap.prototype.search = function(search_for){
	var is_dealer_name = false;
	for(var i in this.markers){		
		if(this.markers[i].name.toUpperCase().indexOf(search_for.toUpperCase()) > -1) is_dealer_name = i;
	}
	if(is_dealer_name) this.open_marker(is_dealer_name);
	else this.localised_search(search_for+', '+this.search_locale);
};
//sorting function
PMap.prototype.number_sort = function (a,b){
  if(a[0] < b[0]) var max = b[0];
  else var max = a[0];
  return 0.4 * (b[1] - a[1]) + 0.6 * ((a[0] - b[0]) / max); //compare service level(80%) and distance(20%) - service level is backwards cause higher service level is better
};
/**
 * with this we replace the existing listing with a list ordered
 * by distance ascending
 */
PMap.prototype.replace_side_bar = function(order, ordered_layers){
	if($('#'+this.side_bar_container).length){
		var list_html = "<ol>";
		for(var i=0; i < order.length; i++){
			var distance = order[i], layer_key = ordered_layers[distance], scaled_distance = parseInt(distance / this.scale);
			list_html+='<li id="'+this.side_id_prefix+layer_key+'" class="'+this.side_class+' clearfix">'+this.markers[layer_key].content.summary+'<p class="'+this.distance_p_class+'">Distance: <strong>'+scaled_distance+' '+this.scale_name+'</strong></p></li>';			
		}
		list_html += "</ol>";
		$('#'+this.side_bar_container+' ol').replaceWith(list_html);
	}
};
//loading class
PMap.prototype.search_loading = function(){
	$('#'+this.search_box_name).removeClass(this.search_failed_class).removeClass(this.search_success_class).addClass(this.search_loading_class);
};
//when search works flag it
PMap.prototype.search_success = function(){
	$('#'+this.search_box_name).removeClass(this.search_loading_class).removeClass(this.search_failed_class).addClass(this.search_success_class);
};
//if the search fails then flag it
PMap.prototype.search_failed = function(){
	this.g_map.removeOverlay(this.search_marker);
	$('#'+this.search_box_name).removeClass(this.search_loading_class).removeClass(this.search_success_class).addClass(this.search_failed_class);
};



