/**
 * @author peter.goulborn
 * Requires OpenLayers, Prototype, atExtendOpenLayers.js
 *
 * $Revision: 142 $
 * $Author: Peter.goulborn $ 
 * 
 * This is a class to provide an Openlayers map for an iSharemaps server.
 */

if( !Astun ) var Astun = {};
if( !Astun.iSharemaps ) Astun.iSharemaps = {};
if( !OpenLayers || !Prototype || !Astun.JS.Common ) throw new Error( 'Dependent library not found' );  // Needs to be nicer, possibly iSharemaps-wide library error?


( function( ) {

	// local properties & objects
	
	// expose OLMap class
	Astun.iSharemaps.OLMap = Class.create( {
		'initialize': function ( elementId , ismurl , options, dataurl ) {
			/*
			 * ARGUMENTS
			 * ---------
			 * elementId - { string } id of the map element
			 * ismurl - {string } iShareMaps map page URL string
			 * options: { object } optional settings for wrapper and all maps
			 *		created by it.
			 *	callbacks: { object } map method container object.
			 *		{ click, doubleclick, hoverPause, hoverMove }
			 * dataurl - { string } iShareMaps data web service URL 
			 * ---------
			 * 
			 * NOTES
			 * -----
			 * Assumes base layer remains the iSharemaps layer.
			 * -----
			 */
			options = options || [];
			this.ismurl = ismurl;
			this.dataurl = dataurl;
			this.getMapService = new Astun.JS.GetData.JSONRequester( 
				this.ismurl,
				'callback',
	    		{
	    			'params': {
						'Type': 'jsonp'
					}
				}
			);
			this.getDataService = new Astun.JS.GetData.JSONRequester( 
				this.dataurl,
				'callback',
	    		{
	    			'params': {
						'Type': 'jsonp'
					}
				}
			);
			
			if( !Astun.appType ) Astun.appType = '';
			
			this.mapElement = $( elementId );
			this.locationZoom = window.astun.settings.locationZoomWidth || 500; // width of map in metres, hardcoded as no setting to govern this  
			
			this.currentLocation = {};
			this.query={};
			this.query.popups = $A( );
			this.installedControls = {};
			this.installedControls.nav = new OpenLayers.Control.Navigation( );
			this.installedControls.navHistory = new OpenLayers.Control.NavigationHistory();
			this.installedControls.lonlat = new OpenLayers.Control.MousePosition( { 'numDigits': 0 } );	
			this.installedControls.keys = new OpenLayers.Control.KeyboardDefaults( );  
			this.installedControls.attrib = new OpenLayers.Control.Attribution( ); 
			this.installedControls.pzb = Astun.appType == 'lite' ? new OpenLayers.Control.PanZoom( ) : new OpenLayers.Control.PanZoomBar( ); 
			this.installedControls.scale = new OpenLayers.Control.ScaleLine( );
			this.timers = {};
			this.timers.CurrentWait = 0 ;
			this.date='';
			
			
			
			this.defaultMapControls = [ 
				this.installedControls.nav,
				this.installedControls.navHistory,
				this.installedControls.lonlat,	
				this.installedControls.keys, 
				this.installedControls.attrib,
				this.installedControls.pzb,
				this.installedControls.scale
			 ];
			
			if( options && options.callbacks ) {
				var clickCallbacks = 	Object.extend( {}, {
					'click': options.callbacks.click, 
					'dblclick': options.callbacks.doubleclick
				} );
				var hoverCallbacks = 	Object.extend( {}, {
					'pause': options.callbacks.hoverPause || function( ){}, 
					'move': options.callbacks.hoverMove || function( ){}
				} );
				this.installedControls.hover = new OpenLayers.Control.Hover( {'delay': astun.settings.popupDelay}, hoverCallbacks );
				this.installedControls.click = new OpenLayers.Control.Click( {'double': false, pixelTolerance: 1}, clickCallbacks ); 
				this.defaultMapControls.push( this.installedControls.hover );
				this.defaultMapControls.push( this.installedControls.click );
			}
			
			this.arThemePopup=[ ];
			
			var mapResize = function( evt ){
					this.openlayers.updateSize( );
			}
			
			/* Event listeners */
			Event.observe( this.mapElement, 'astun:mapResize', mapResize.bindAsEventListener( this ) );
			
			
			
		},
		'addControl': function( controlName, control ) {
			this.openlayers.addControl( control );
			this.installedControls[ controlName ] = control;
			this.installedControls[ controlName ].activate( );
		},
		'removeControl': function( controlName ) {
			var removed = false;
			var control = this.installedControls[ controlName ];
			if( !!control ) {
				removed = this.installedControls[ controlName ].deactivate( );
				this.openlayers.removeControl( control );
			} 
			return removed;
			
		},
		'setControl': function( controlName, control ) {
			this.removeControl( controlName );
			return this.addControl( controlName,control );
		}
		,
		'loadMap': function( baseMapSource, dataMapSource, options ) {
			/* 
			 * baseMapSource: {Object} Javascript representation of a base 
			 *     map source
			 * dataMapSource: {Object} Javascript representation of a data
			 *     map source
			 * options: {Object} container for optional settings:
			 * 		initialView: {Object} ISM view( easting, northing, zoom}.
			 * 		initialViewType: {String} Whether supplied initial view or default
			 *			view is used instead of cookie view. ).
			 *		data {Object}: Optional object with 
			 *			{<OpenLayers.Layer.iShareMaps>} properties to tag onto
			 *			the layer.
			 */
			var resScaleProperties = $A( [ 'scales','resolutions','maxResolution','minScale' ] );
			if( !options ) {
				var options = {};
			}
			if( options.forceInitial ) {
				options.initialViewType = 'supplied';  //catch old way of doing this.
			}
			if( options.initialViewType !== 'default' && options.initialViewType !== 'supplied' ) {
				options.initialViewType = 'cookie';
			}
			this.mapSource = dataMapSource;
			var bounds = new OpenLayers.Bounds.fromArray( baseMapSource.bounds );	
			var resBounds =  new OpenLayers.Bounds.fromArray( dataMapSource.bounds );
			var mapOptions = {
				'restrictedExtent': resBounds,
				'projection':  new OpenLayers.Projection('EPSG:27700'),
				'displayProjection':  new OpenLayers.Projection('EPSG:27700'),
				'units': 'm',
				'maxExtent': bounds,
				'controls': this.defaultMapControls
			};
			
			if( baseMapSource.baseMapDefinition.scales && baseMapSource.baseMapDefinition.scales.length ) {
				mapOptions.scales = baseMapSource.baseMapDefinition.scales;
				mapOptions.numZoomLevels = baseMapSource.baseMapDefinition.scales.length;
			}
			else {
				mapOptions.maxresolution = 50; //units per pixel
				mapOptions.numZoomLevels = 9;
			}
			
			this.openlayers = new OpenLayers.Map( 
				this.mapElement, 
				mapOptions
			 );
			this.map = this.openlayers; //backwards compatibility
			this.openlayers.wrapper = this; // this is needed but ideally shouldn't be necessary...
			
			
			for( var c = 0; c < this.openlayers.controls.length; c++ ) {
				this.openlayers.controls[ c ].activate( );
			}		
			
			var defaultLayerOptions =  {
				'projection': this.openlayers.projection,
				'units': this.openlayers.units,
				'maxExtent': this.openlayers.maxExtent
			}
			while( resScaleProperties.length ) {		
				var property = resScaleProperties.pop( );	
				var value = this.openlayers[ property ];
				if( value ) {
					defaultLayerOptions[ property ] = value;
				}
			}
			
			var setISMLayer = function( dataMapSource)	{
				if( this.ismDataLayer ) {
					var oldISMDataLayer = this.ismDataLayer;
				}
				
				var ismOptions = Object.extend( {},defaultLayerOptions );
				Object.extend( 
					ismOptions, 
					{
						'singleTile': true,
						'ratio': 1.4,
						'alpha':true,
						'maxExtent': new OpenLayers.Bounds.fromArray( dataMapSource.bounds )
					}
				);
				
				this.ismDataLayer = new OpenLayers.Layer.iShareMaps( 
					'iSharemaps Data Layer', 
					this.ismurl, 
					{
						'mapsource': dataMapSource.mapName,
						'transparent': true
					}, 
					( options.data ) ? Object.extend( ismOptions, options.data ) : ismOptions
				);
				
				this.mapSource = dataMapSource;
				
				this.openlayers.addLayer( this.ismDataLayer );  
				this.openlayers.setLayerIndex( this.ismDataLayer, 1 ); //always one above bottom
				this.setISMLayers( 
					( options.data && options.data.defaultLayers ) ? options.data.defaultLayers : [ ],
					( options.data && options.data.defaultFilter ) ? options.data.defaultFilter : null
				);
				if( !this.ismDataLayer.params.layers.length ) {
					this.ismDataLayer.visibility = false;
				}
				if( oldISMDataLayer ) {
					oldISMDataLayer.destroy();
					oldISMDataLayer = null;
					this.mapElement.fire( 'astun:mapLoaded', dataMapSource );  //map has reloaded...
				}
			
			
			}.bind( this )
			
		
			
			
			var setBaseMap = function( baseMapSource ) {
				var definition = baseMapSource.baseMapDefinition;
				var rasterLayerName = definition.name;			
				var rasterType = ( definition.type ) ? definition.type : 'WMS';
				
				if( this.rasterLayer ) {
					var oldRasterLayer = this.rasterLayer;
					var oldResolution = this.map.getResolution();
				}
				
				var rasterOptions = Object.extend( {}, defaultLayerOptions );
				var wmsOptions = {
					'layers': rasterLayerName
				}
				if( definition.options ) {
					rasterOptions = Object.extend( rasterOptions, definition.options );
					wmsOptions.FORMAT = definition.options.format;
				}
				Object.extend( 
					rasterOptions, 				 
					{
						'buffer': 1,
						'transitionEffect': 'resize',
						'scales': definition.scales,
						'attribution': definition.copyright,
						'maxExtent': new OpenLayers.Bounds.fromArray( baseMapSource.bounds )
					}
				);
				this.map.maxExtent = rasterOptions.maxExtent;
				if( definition.tilecaches && definition.tilecaches.length) {
					rasterType = 'TileCache';
				}
				
				
				switch( rasterType ) {
					case 'WMS':
						this.rasterLayer = new OpenLayers.Layer.WMS( 
							'iShareMaps Raster Layer',
							definition.uri,
							wmsOptions,
							rasterOptions
						);
						break;
					case 'TileCache':
						this.rasterLayer = new OpenLayers.Layer.TileCache( 
							'iShareMaps Raster Layer',
							definition.tilecaches,
							rasterLayerName,
							rasterOptions
						);
						break;
					default:
						throw( 'Invalid raster layer type specified' );
						break;
				}
				this.rasterLayer.events.register('loadend', this.rasterLayer, function(evt) {
					jQuery('#' + this.map.wrapper.mapElement.id ).trigger('reasterLayerLoaded');
					astun.isReasterLayerLoaded = true;
				})
				this.map.addLayer( this.rasterLayer );  
				this.map.setBaseLayer( this.rasterLayer );
				
				if( oldRasterLayer ) {
					oldRasterLayer.destroy();
					oldRasterLayer = null;
					this.map.zoomTo( this.map.getZoomForResolution( oldResolution ) );
				}
				
				
			}.bind( this )
			
			setBaseMap( baseMapSource );
			setISMLayer( dataMapSource );
			
			this.markerLayer = new OpenLayers.Layer.Markers( 'Markers', {'maxResolution': this.openlayers.maxResolution, 'maxExtent': this.openlayers.maxExtent} );
			this.openlayers.markerLayer = this.markerLayer; //map.markerLayer is deprecated please use markerlayer instead.
			this.openlayers.addLayer( this.markerLayer );
			
			
			
			
			var setView = function( mapView ){
				if( options.initialViewType === 'supplied' || !mapView ) {
					var view = options.initialView;
				}
				else if( options.initialViewType === 'cookie' ){
					var view = mapView.evalJSON( );
				}
				
				if( view ) {
					this.draw( view );
				}
				else {
					// initialViewType === 'default'
					this.draw( bounds.toArray() );
				}
			}
			if( !options.deferDraw ) {
				this.mapElement.fire( 'astun:loadSetting', {setting: 'mapView', loadFunction: setView.bind( this )} );
			}
			
			
			this.openlayers.events.register( "moveend", this, function( ) {
				// Create iSharemaps 'view' from centre point plus image width and resolution
				var en = this.openlayers.getCenter( );
				var imageSize = this.ismDataLayer.getImageSize( );
				var view = {
					'easting': en.lon,
					'northing': en.lat, 
					'scale': this.openlayers.getScale(),
					'extent': this.openlayers.getExtent().toArray(),
					'zoom' : imageSize.w * this.ismDataLayer.getResolution( )
				};
				var json = Object.toJSON( view );
				this.mapElement.fire( 'astun:saveSetting', {setting: 'mapView', value: '' + json} );

				//Trigger an jQuery event that map is moved
				if (jQuery )
				{
					jQuery('#' + this.mapElement.id )
						.trigger('mapMoved',[ view ] )
						.trigger('moveBackMap',true)
						.trigger('adjustPopUp');
				}
				
            } );
            
            // If location set, show position on map
			if (this.currentPosition) {
				this.showLocation(this.currentPosition);
			}
			
            
            /* Set up event listeners */
            var mapMove = function( evt ) {
				/**
				 * Function: mapMove 
				 * Event wrapper for this.draw
				 *
				 * Parameters:
				 * evt - {object} Prototype Event, memo property expected
				 *		to be { easting, northing[ , zoom ] } view object.
				 *
				 * Returns:
				 * nothing
				 */
				this.draw( evt.memo );
            }
            
            
            
            var setPosition = function( evt ) {	
				/**
				 * Function: setPosition
				 * Event wrapper for this.draw 
				 *
				 * Parameters:
				 * evt - {object} Prototype Event, memo property expected
				 *		to be { address, uid, x, y } address object.
				 *
				 * Returns:
				 * nothing
				 */			
				if( evt.memo.x && evt.memo.y ) {
					this.currentPosition = new OpenLayers.LonLat( evt.memo.x, evt.memo.y );
					this.draw({ 
						'easting': evt.memo.x, 
						'northing': evt.memo.y, 
						'zoom': this.locationZoom
					} );
				}
            }
            
            var setAddress = function( evt ) {
				/**
				 * Function: setAddress 
				 * Event wrapper for this.showLocation and setPosition
				 *
				 * Parameters:
				 * evt - {object} Prototype Event, memo property expected
				 *		to be { address, uid, x, y } address object.
				 *
				 * Returns:
				 * nothing
				 */
				if( evt.memo.x && evt.memo.y ) {
					var en = new OpenLayers.LonLat( evt.memo.x, evt.memo.y );
					this.currentLocation = evt.memo;
					this.showLocation( en );
					Astun.JS.Common.setCookie('astun:currentLocation', Object.toJSON( evt.memo ), 100 ,(astun.intranetApp) ? '' : '/', '');
					setPosition.bindAsEventListener( this )( evt );
				}
            }
            
			Event.observe( this.mapElement, 'astun:mapMove', mapMove.bindAsEventListener( this ) );
			Event.observe( this.mapElement, 'astun:setAddress', setAddress.bindAsEventListener( this ) );
			//Change the cursor during the ajax calss
			//Event.observe( this.mapElement, 'astun:dataLoadBegin', function(){ jQuery('#atMap').css('cursor', 'wait'); } );
			//Event.observe( this.mapElement, 'astun:dataLoadComplete', function(){ jQuery('#atMap').css('cursor','auto') } );
			
			//Event binding for jQuery
			if( jQuery )
			{
				var $eventElement = jQuery('#' + this.mapElement.id );
				
				// style the sketch fancy
				var sketchSymbolizers = {
					"Point": {
						pointRadius: 4,
						graphicName: "square",
						fillColor: "white",
						fillOpacity: 1,
						strokeWidth: 1,
						strokeOpacity: 1,
						strokeColor: "#333333"
					},
					"Line": {
						strokeWidth: 3,
						strokeOpacity: 1,
						strokeColor: "#666666",
						strokeDashstyle: "dash"
					},
					"Polygon": {
						strokeWidth: 2,
						strokeOpacity: 1,
						strokeColor: "#666666",
						fillColor: "white",
						fillOpacity: 0.3
					}
				};
				var style = new OpenLayers.Style();
				style.addRules([ new OpenLayers.Rule({symbolizer: sketchSymbolizers}) ]);
				var styleMap = new OpenLayers.StyleMap({"default": style});
				
				function handleMeasurements(event) {
					var geometry = event.geometry;
					var units = event.units;
					var order = event.order;
					var measure = event.measure;
					var $element = jQuery('#atMapOutPut');
					var out = "";
					if(order == 1) {
						out += "Distance: " + measure.toFixed(3) + " " + units;
					} else {
						out += "Area: " + measure.toFixed(3) + " " + units + '<sup style="position:absolute;padding-left:2px">2</' + 'sup>';
					}
					$element.show().animate({right:0} ,800).find('.atQuickInfo').html(out);
					if(!$element.is(':animated')) $element.effect('highlight', {}, 1000);
				}
				
				function handleSelections(event) {
					
					//astun.mapWrapper.interactions.getNearestFeatures()
					//jQuery('#atgeoInfo').remove();
					//jQuery('<div id="atgeoInfo" />').addClass('ui-state-default ui-corner-all').css({'position':'absolute', 'top':5, 'right':5,'width':260,'font-size':13,'z-index':100000,'padding':10}).appendTo('body').html(geometry.toString());
				}
				
				
				
				$eventElement
				.bind('loaderDialogVisibility', this, function( evt, visibility ) {
					visibility ? jQuery('#atMapLoader').fadeIn('fast') : jQuery('#atMapLoader').fadeOut('fast');
				})
				.bind('mapSourceLoaded', this, function( evt, mapSource, type ) {
					if( type === 'raster' ) {
						setBaseMap( mapSource );
					} 
					else if( type === 'ism' ) {
						setISMLayer( mapSource );
					} 				
				})
				.bind('mapMove', this, function( evt, location ) {
					evt.data.draw( location );
				})
				.bind('moveMap', this, function( evt, location ) {
					evt.data.draw( location );
				})
				.bind('setAddress', this, function( evt, address ) {
					evt.data.setAddress( address );
				})
				.bind('searchISM', this, function( evt, layer, field, value ) {
					evt.data.searchISMLayer( layer, field, value,  function( response, type, queryOptions ) {
						if( response.unexpectedResponse ) {
							alert('No results found');
						}
						else {
							evt.data.showResults( response, type, queryOptions );
						}
					} );
					//Hide the loader
					$eventElement.trigger('loaderDialogVisibility', false);
				})
				.bind('clearResults', this, function( evt ) {
					evt.data.clearPopups();
					evt.data.mapElement.fire("astun:resultsCleared", {'type': '', 'displayName': evt.data.query.layer} );
					evt.data.query.layer = null;
				})
				.bind('showChangeViewDialog', this, function( evt ) {
					//Load the cookie settings and take the CURRENT northing, easting and zoom level and populate the textboxes			 
						evt.data.mapElement.fire('astun:loadSetting',{
							setting		: 'mapView',
							loadFunction: function(cords){
											var cords = (!!cords) ? cords.evalJSON() : false;
											currentNorthing = cords.northing || '';
											currentEasting = cords.easting || '';
											currentZoom = cords.zoom || '';
										  }
						});
					//If there is a dialog already appended remove it - so in following lines it will redraw
					if(jQuery('.atDialogAlert').length!=0) jQuery('.atDialogAlert, .ui-widget-overlay:last').remove();
					
					//Append an empty div at the end of the body which will be converted to dialog
					jQuery('<div></div>')
						.addClass('atDialogAlert')
						.attr('title','Change view')
						.html(jQuery('<h4></h4>')
									 .html('<i>Please provide following details:</i>')
									 .css({'padding':2, 'margin-bottom':5, 'margin-top':0})
							 )
						.append(
							//Easting Box
							jQuery('<label></label>')
								.attr('for','atChangeViewEasting')
								.css({ 'display':'block', 'width':75, 'float':'left', 'padding':2 })
								.html('Easting:'),
							jQuery("<input name='atChangeViewEasting' id='atChangeViewEasting' />")
								.val(currentEasting)
								.css({'padding':2, 'margin-bottom':4})
								.keydown(function(e) { Astun.JS.Common.stopEventProp(e); })
								.addClass('ui-state-active ui-corner-all'),
							'<br />',
							//Northing box
							jQuery('<label></label>')
								.attr('for','atChangeViewNorthing')
								.css({ 'display':'block', 'width':75, 'float':'left', 'padding':2 })
								.html('Northing:'),
							jQuery("<input name='atChangeViewNorthing' id='atChangeViewNorthing' />")
								.val(currentNorthing)
								.css({'padding':2, 'margin-bottom':4})
								.keydown(function(e) { Astun.JS.Common.stopEventProp(e); })
								.addClass('ui-state-active ui-corner-all'),
							'<br />',
							jQuery('<label></label>')
								.attr('for','atChangeViewZoom')
								.css({ 'display':'block', 'width':75, 'float':'left', 'padding':2 })
								.html('Map width'),
							jQuery("<input name='atChangeViewZoom' id='atChangeViewZoom' />")
								.val(currentZoom)
								.css({'padding':2, 'margin-bottom':4})
								.keydown(function(e) { Astun.JS.Common.stopEventProp(e); })
								.addClass('ui-state-active ui-corner-all'),
							'<br />',
							jQuery('<label></label>').css({ 'display':'block', 'width':75, 'float':'left', 'padding':2 }),
							jQuery("<button>Cancel</button>")
									.addClass('ui-state-default ui-corner-all')
									.css('margin-right',4)
									.bind('mouseenter mouseleave',function(){ jQuery(this).toggleClass('ui-state-hover') })
									.keydown(function(e) { Astun.JS.Common.stopEventProp(e); })
									.click(function(){
										jQuery('.atDialogAlert').dialog('close');
									}),
							jQuery("<button>Go</button>")
								.addClass('ui-state-default ui-corner-all')
								.bind('mouseenter mouseleave',function(){ jQuery(this).toggleClass('ui-state-hover') })
								.keydown(function(e) { Astun.JS.Common.stopEventProp(e); })
								.click(function(){
									var n = jQuery('#atChangeViewNorthing'),
										e = jQuery('#atChangeViewEasting'),
										z = jQuery('#atChangeViewZoom'),
										error = false;
									//validation
									jQuery('.ui-dialog:last').find('input').each(function(){
										if(jQuery.trim(jQuery(this).val()).length==0)
										{
											jQuery(this).addClass('ui-state-error');
											error=true;
										}
										else jQuery(this).removeClass('ui-state-error')
										
										//OnBlur check for the errors again
										jQuery(this).blur(function(){ jQuery('.ui-dialog:last button:first').trigger('click') })
									})
									//If error found
									if(error) return false;
									//Location from the textboxes
										var loc = {
													"easting"	: jQuery.trim(e.val()),
													"northing"	: jQuery.trim(n.val()),
													"zoom"		: jQuery.trim(z.val())
												  }
									//Move the map
										$eventElement.trigger('mapMove', loc);
									//Close the dialog
										jQuery('.atDialogAlert').dialog('close');
								})
						 )
						.appendTo('body')
						.dialog({
								modal: true,
								resizable: false
						 });
							
						//Fix the font size and z-index
							jQuery('.ui-widget-overlay:last').css('z-index',10000000)
							jQuery('.ui-dialog:last').css({'font-size':12, 'z-index':10000000})
				})
				.bind('navigation',  function( evt,navType) {
					var navControl = astun.mapWrapper.installedControls.navHistory,
						p, n;
					/*Change the status of the navigation
					  Note: We put setTimout just to wait for mapMove event to finish its events.
					*/
					setTimeout(function(){
						//Make both true/active
							$eventElement.trigger('moveBackMap',true);
							$eventElement.trigger('moveForwardMap',true);
						if(navType=='back')
						{
							navControl.previousTrigger();
							if(!navControl.previousTrigger()) $eventElement.trigger('moveBackMap',false);
						}
						else
						{
							navControl.nextTrigger();
							if(!navControl.nextTrigger()) $eventElement.trigger('moveForwardMap',false);
						}
					},500)
				})
				.bind('selectionControls', this, function( evt,selectionType,status) {
					switch(selectionType)
					{
						case 'circle':
									//Activate the control
									var circleControl = new OpenLayers.Control.Measure
									(
										OpenLayers.Handler.RegularPolygon,
										{
											persist: true,
											handlerOptions: {sides:40}
										}
									)
									
									//Activate the control
									astun.mapWrapper.setControl('drag', circleControl);
									
									//Attach the event which will be triggered when user finishes 
									circleControl.events.on({
										"measure": function(event)
										{
											//Callback function
											astun.mapWrapper.interactions.getNearestFeatures(event);
											//Disable tool
											$eventElement.trigger('circleStatus', false);
											$eventElement.trigger('polygonStatus',false);
											//Also remove control from the map
											$eventElement.trigger('mapControls');
										}
									});
									
									//Trigger events to toggle the selection tools
									$eventElement.trigger('circleStatus', !status ? true : false);
									$eventElement.trigger('polygonStatus',false);
									break;

						case 'polygon':
									//Activate the control
									var polygonControl = new OpenLayers.Control.Measure(OpenLayers.Handler.Polygon);
									
									//Activate the control
									astun.mapWrapper.setControl('drag', polygonControl);
									
									//Attach the event which will be triggered when user finishes 
									polygonControl.events.on({
										"measure":  function(event)
										{
											//Callback function
											astun.mapWrapper.interactions.getNearestFeatures(event);
											//Disable tool
											$eventElement.trigger('circleStatus', false);
											$eventElement.trigger('polygonStatus',false);
											//Also remove control from the map
											$eventElement.trigger('mapControls');
										}
									});
									
									//Trigger event to toggle the selection tools
									$eventElement.trigger('polygonStatus', !status ? true : false);
									$eventElement.trigger('circleStatus',false);
									break;	
					}
					
					//If status is true this means that tool is de-selected so just activate the default map controls 
					if(status)
					{
						$eventElement.trigger('mapControls');
					}
				})
				.bind('mapControls', this, function( evt,dragType,status) {
					if(dragType == 'DragPan' && status)
					{
						//Default
						dragType = 'ZoomBox';
						status = false;
					}

					$eventElement.trigger('hideQuichInfoBox');
					
					switch(dragType)
					{
						case 'ZoomBox':
								if(!status)
								{
									astun.mapWrapper.setControl('drag', new OpenLayers.Control.ZoomBox());
								    astun.mapWrapper.setControl('shiftdrag', new OpenLayers.Control.DragPanExt({keyMask:OpenLayers.Handler.MOD_SHIFT}));
									$eventElement.trigger('zoomStatus',true);
									$eventElement.trigger('panStatus',false);
									$eventElement.trigger('distanceStatus',false);
									$eventElement.trigger('areaStatus',false);
									break;
								}
						case 'distance' :
								if(!status)
								{	
									var lineControl = new OpenLayers.Control.Measure
									(
										OpenLayers.Handler.Path, {
											persist: true,
											handlerOptions: {
												layerOptions: {styleMap: styleMap}
											}
										}
									)
									astun.mapWrapper.setControl('drag', lineControl);
									
									lineControl.events.on({
										"measurepartial": handleMeasurements,
										"measure": handleMeasurements
									});
									
									$eventElement.trigger('distanceStatus',true);
									$eventElement.trigger('areaStatus',false);
									$eventElement.trigger('zoomStatus',false);
									$eventElement.trigger('panStatus',false);
									break;
								}
						case 'area' :
								if(!status)
								{
									var areaControl = new OpenLayers.Control.Measure(
										OpenLayers.Handler.Polygon, {
											persist: true,
											handlerOptions: {
												layerOptions: {styleMap: styleMap}
											}
										}
									)
									astun.mapWrapper.setControl('drag', areaControl);
									
									areaControl.events.on({
										"measure": handleMeasurements
									});

									$eventElement.trigger('areaStatus',true);
									$eventElement.trigger('distanceStatus',false);
									$eventElement.trigger('zoomStatus',false);
									$eventElement.trigger('panStatus',false);
									break;
								}
						default:
								astun.mapWrapper.setControl('drag', new OpenLayers.Control.DragPanExt());
								astun.mapWrapper.setControl('shiftdrag', new OpenLayers.Control.ZoomBox({keyMask:OpenLayers.Handler.MOD_SHIFT}));
								astun.mapWrapper.setControl('click', astun.mapWrapper.installedControls.click);
								$eventElement.trigger('panStatus',true);
								$eventElement.trigger('zoomStatus',false);
								$eventElement.trigger('distanceStatus',false);
								$eventElement.trigger('areaStatus',false);
								break;
					}
				})
				.bind('resizeMapElement', this, function( evt,mapWidth) {
						var centre = evt.data.map.getCenter(); //lonlat of pre-resize map 
						
						if(mapWidth && mapWidth!='') $eventElement.width(mapWidth);
						if( centre ) {
							var view = {easting: centre.lon, northing: centre.lat};
							evt.data.draw(view);
						}
						//Also change the position of the loader
						$eventElement.trigger('repositionLoader');
				});
			}
			/* End event listener setup */
			
			
			
			
			
			
			this.ismDataLayer.events.register( "loadstart", this.ismDataLayer, function( ) {
                this.map.wrapper.mapElement.fire( "astun:layerStart", {layer: this} );
            } );
			this.ismDataLayer.events.register( "loadend", this.ismDataLayer, function( ) {
                this.map.wrapper.mapElement.fire( "astun:layerEnd", {layer: this} );
            } );
			this.mapElement.fire( 'astun:mapLoaded', dataMapSource );
			
			//Trigger jQuery event saying map is loaded
			if( jQuery )
			{
				jQuery('#' + this.mapElement.id ).trigger('mapLoaded',this);
			}
			
		},
		'setISMLayers': function( layers, filter ) {
			/*
			 * layers -> iSharemaps layer names array
			 */
			this.ismDataLayer.params.layers = $A( layers );
			
			if( filter ) {
				this.ismDataLayer.params.filterlayers = filter;
			}/*
			else if( this.ismDataLayer.params.filterlayers ) {
				delete this.ismDataLayer.params[ 'filterlayers' ];
			}*/
		},
		'getISMLayers': function( ) {
			/*
			 * layers -> iSharemaps layer names array
			 */
			return this.ismDataLayer.params.layers;
		},
		'removeISMLayer': function( layer ) {
			/*
			 * layer -> layer name string
			 */
			this.ismDataLayer.params.layers = this.ismDataLayer.params.layers.without( layer );
		},
		'addISMLayer': function( layer, filter ) {
			/*
			 * layer -> layer name string
			 */
			this.ismDataLayer.params.layers.push( layer );
			
				
		}, 
		'createMarker': function( position, icon ) {
		/**
		* Function: createMarker 
		* Draws a marker on the marker layer on the map 
		*
		* Parameters:
		* position - {object} OpenLayers.LonLat object
		* icon - OpenLayers.Icon object( defaults to default OpenLayers icon )	
		*
		* Returns:
		*( object} - OpenLayers.Marker object
		*/
			var marker = new OpenLayers.Marker( position, icon || OpenLayers.Marker.defaultIcon( ) );
			this.openlayers.markerLayer.addMarker( marker );
			//set marker layer to be top one
			this.openlayers.markerLayer.setZIndex( this.openlayers.Z_INDEX_BASE[ 'Popup' ] - 1 ); 
			
			return marker;	
			 
		},
		'deleteMarker': function( marker ) {
		/**
		* Function: deleteMarker 
		* Removes a specific marker from the display and destroys the object 
		*
		* Parameters:
		* marker - {object} OpenLayers.Marker object
		*
		* Returns:
		*/
			if( marker ) {
				this.openlayers.markerLayer.removeMarker( marker );
				marker.destroy( );
				marker = null;
			}
		},
		'clearPopups': function( ) {
		/**
		* Function: clearPopups 
		* Removes removes all popups from the display and destroy them 
		*
		* Parameters:
		*
		* Returns:
		*/
			if( this.openlayers.popups.length > 0 ) {
				while( this.openlayers.popups.length )
				{
					var popup = this.openlayers.popups.pop( );
					this.openlayers.removePopup( popup );
					popup.destroy( );
					popup = null;
				}
			}
		},
		'draw': function ( view )	{
		/**
		* Function: draw 
		* Draws or redraw the map.
		* Recenters if coordinates are specified,
		* rezooms if iSharemaps zoom level is specified, 
		* redraws iSharemaps layer, so any other changes to parameters take effect.
		*
		* Parameters:visib
		* view	- {Object} co-ordinates with old iShareMaps 'zoom': { easting, northing, zoom }
		*		- {Object} co-ordinates with scale: { easting, northing, scale }
		*		- [Array] array of bounding box co-ordinates: [ minX, minY, maxX, maxY ] 
		* Returns:
		*/
		    
			if( view && ( view.easting || view.northing ) ) {
					// recentre map on provided co-ordinates
				var newCentre = OpenLayers.Util.extend( this.openlayers.getCenter( ) || new OpenLayers.LonLat( ), {'lon': view.easting - 0.0, 'lat': view.northing - 0.0	} );
				var imageSize = this.ismDataLayer.getImageSize( );
				var currentResolution = this.ismDataLayer.getResolution( );
				if( view.scale ) {
					view.scale = view.scale - 0.0;
					var resolution =  view.scale * this.openlayers.getResolution() / this.openlayers.getScale();
				}
				else if( view.zoom && view.zoom != imageSize.w * currentResolution ) {
					var resolution = view.zoom / imageSize.w;
				}
				else {
					var resolution = currentResolution;
				}
				this.openlayers.setCenter( 
					newCentre, 
					this.openlayers.getZoomForResolution( resolution ),
					false,
					true
				);
			}
			else if( view && Astun.JS.Common.isArray( view ) && view.length === 4) {
				var newBounds = OpenLayers.Bounds.fromArray( view ); 
				this.openlayers.zoomToExtent( newBounds );
			} 
			else 
			{
				window.olmap = this;
				
				if( this.timers.CurrentWait )
					clearTimeout( this.timers.CurrentWait );
			    
				this.timers.CurrentWait = setTimeout( "window.olmap.ismDataLayer.redraw( );",300 );
				/*
				window.olmap = this;
				 var timeOutVar = setTimeout( "window.olmap.ismDataLayer.redraw( )",500 );
				
				//this.ismDataLayer.redraw( );		
				*/		
			}
		
		},
		'getGeoJSONData' : function( params, callbackFunction ) {
		/**
		* Function: getGeoJSONData 
		* Makes a Ajax call with supplied params to return GeoJSON data
		*
		* Parameters:
		* params - {object ) params object as detailed below
		* callbackFunction - {object ) function object to be called on sucess
		*
		* Returns:
		*/
			var defaultParams = {
				'MapSource': this.mapSource.mapName,
				'RequestType': 'GeoJSON',
				'ServiceAction': '',
				'ActiveTool': '',
				'ActiveLayer': '',
				'mapid': -1,
				'axuid': new Date( ).valueOf( )
			};	
			var parameters = Object.extend( defaultParams, params );
			var thisPointer = this ;
			var queryOptions = {'params': parameters, 'url':this.getMapService.uri, 'isFirstResult':true}
							
			this.mapElement.fire( 'astun:dataLoadBegin', {} );
			
			this.getMapService.request( 
				parameters, 
				function( json ) {
					if( json[0] && json[0].type && json[0].type == 'FeatureCollection' ) {
						callbackFunction( json, thisPointer, queryOptions );
					}
					else if( json.error ) {
						callbackFunction( { 'unexpectedResponse': json.error.response, 'error': json.error }, thisPointer ) ; 
					}
					else {
						callbackFunction( { 'unexpectedResponse': 'Unknown response', 'response': json }, thisPointer ) ; 
					}
					this.mapElement.fire( 'astun:dataLoadComplete', {} );
				}.bind( this )
			 );
		
			/*
			var successFunc = function( transport ) 
			{
				//check if return string contains a valid collection
				var strResponse = transport.responseText ; 
				
				if( strResponse.search( /FeatureCollection/ ) > 0 )
				{
					var results = transport.responseText.evalJSON( );							
					callbackFunction( results, thisPointer ) ; 
				}
				else
				{						
					callbackFunction( {'unexpectedResponse': transport.responseText}, thisPointer ) ; 
				}
				
				this.mapElement.fire( 'astun:dataLoadComplete', {} );
	
			};	 
			
			new Ajax.Request 
			( this.ismurl, 
				{
					method: 'get',
					parameters: parameters,
					onFailure: function( transport ) 
					{
						alert( "Error: Failed to get geometry data!" );
						//window.alert( "FAILURE: Could not access iShareMaps search page!" );	
					},
					onSuccess : successFunc.bindAsEventListener( this )			
				}
			 ); //Ajax request 
			 */
		},
		
		'getResultsPerLayer' : function(serverErrorLayers,isFirstTime,numOfLayers,filteredArr,parameters,thisPointer,queryOptions,callbackFunction)
		{
		/**
		* Function: getResultsPerLayer
		* Returns collection of underlying map features for each layer one by one
		*
		* Parameters:
		*
		* Returns:
		*/
			//Check if search is not cancelled
			var atTimer=0;

			//If layers has no item than just return
			if(filteredArr.length==0 || astun.searchCancelled)
			{
				//debug('Search finished');
				//Clear the timer
				clearTimeout(atTimer);
				//Check if the totall number of layers and the number of layers for which the no results found are equal
				if(numOfLayers==astun.numResultsFoundForLayers)
				{
					callbackFunction({ 'unexpectedResponse': 'No results found for any layer.', 'error': true }, thisPointer, queryOptions );
				}
				this.mapElement.fire( 'astun:dataLoadComplete', {} );
				//Remove the progress div from the loader
				jQuery('#atMapLoader').find('#atLoaderProgress, #atCnlSrch').remove();
				//Change Searching msg back to Loading...
				jQuery('#atMapLoader #atLoaderText').html('Loading...');
				return;
			}
			//Also add a query option to tell the result set that these are the results for the first layer
			queryOptions.isFirstResult = (isFirstTime); //Will return true on first time but will be false soon the first time a layer get result - see it is getting false in below callback function
			//Change the 'Layers' parameter of the object and just this one layer
			parameters.Layers = filteredArr[0];
			//Add the progress div to loader so user can see the progress
			jQuery('#atMapLoader').find('#atLoaderProgress, #atCnlSrch').remove();
			//var progress = (parseInt(parseInt((numOfLayers-filteredArr.length)*100,10)/numOfLayers,10)); //Progress in percentage
			var progress = (numOfLayers+1-filteredArr.length)+' of '+(numOfLayers); //Progress in layers count => Searching layer 1 of 5
			//Change loading msg to Searching...
			jQuery('#atMapLoader #atLoaderText').html('Searching');
			jQuery('#atMapLoader').append(
				'<div id="atLoaderProgress" style="font-size:11px">layer '+ progress +'</div>',
				jQuery('<a href="#" id="atCnlSrch">Cancel search</a>')
					.css({'font-size':11, 'display':'block','padding-top':3,'text-decoration':'underline','color':'#CD0A0A'})
					.click(function(){
						astun.searchCancelled = true;
						//debug('Cancel button clicked');
						astun.mapWrapper.getResultsPerLayer(serverErrorLayers,isFirstTime,numOfLayers,filteredArr,parameters,thisPointer,queryOptions,callbackFunction);
						return false;
					})
			);
			
			
			//Check the progress after 60 secs and if the progress is the same than just issue a timeout for this layer
			atTimer = setTimeout(function()
			{
				//Return if search is already cancelled
				if(astun.searchCancelled) return;
				//Add the layername to array
				serverErrorLayers.push(parameters.Layers);
				//Once we get the request, call itself after removing the Searched Layer from the array
				filteredArr.shift();
				//Call itself again
				astun.mapWrapper.getResultsPerLayer(serverErrorLayers,isFirstTime,numOfLayers,filteredArr,parameters,thisPointer,queryOptions,callbackFunction);
			}
			,60000);

			//Make a request (jsonp) to get the results
			this.getMapService.request( 
				parameters,
				function(json) {
					//Return if search is already cancelled
					if(astun.searchCancelled) return;
					if(typeof json.error=='undefined')
					{
						/*Check if the layer name is already in the list of serverErrorLayers array which means that
						  result is already sent (too late sorry)*/
						if(jQuery.inArray(json[0].properties.layername, serverErrorLayers)!=-1)
						{
							//debug('Late response found for: '+ json[0].properties.layer)
							return;
						}
					}
					
					//First clear timer because we got the results and the request is not set timeout
					clearTimeout(atTimer);
					
					//Check the response for validity
					if( json[0] && json[0].type && json[0].type == 'FeatureCollection' )
					{
						callbackFunction( json, thisPointer, queryOptions);
						isFirstTime = false;//Results found so make it false for the next loop
					}
					else
					{
						astun.numResultsFoundForLayers++;
					}
					
					//Once we get the request, call itself after removing the Searched Layer from the array
					filteredArr.shift();
					//Call itself again
					astun.mapWrapper.getResultsPerLayer(serverErrorLayers,isFirstTime,numOfLayers,filteredArr,parameters,thisPointer,queryOptions,callbackFunction);
				}.bind(this)
			);
			
		},

		'getMapMultiInfo': function( coordinates, pxTolerance, queryType , callbackFunction )
		{
		/**
		* Function: getMapMultiInfo 
		* Returns collection of underlying map features on mouse-click and mouse hoverPause
		*
		* Parameters:
		* coordinates - {object } / { string } OpenLayers.LonLat object OR string with all the corrdinates for a shape
		* pxTolerance - {number} the pixel tolerance for selection of features, ignore those beyond
		* queryType - {string ) the nature of query which will determine features to be returned
		* callbackFunction - {object} Reference to the function to be called on success
		*
		* Returns:
		*/
			
			//Filter the results. For details look at http://intranet.astun.local/docs/development:layers_catalog_specification#info_click_issue
			//Create the cookie name
			var $eventElement = jQuery('#'+this.mapElement.id),
				mapSource = $eventElement.data('currentlyActiveMapSource').replace(/[\s\/]+/g, '_') || '',
				layersResultsCookieName = 'rLayers_'+mapSource,
				rLayers = false,
				qLayers = this.ismDataLayer.params.layers.toArray(),
				filteredArr = new Array();
			
			//Now load the cookie value
			this.mapElement.fire('astun:loadSetting',
			{
				setting: layersResultsCookieName,
				loadFunction:function(layers)
				{
					//Check if layers is null that means cookie is not there so create the cookie and add the layers to it
					rLayers = (typeof layers=='undefined' || layers=='undefined' || layers==null) ? false : layers;
				}
			});
			
			/*
			  Now we have saved all those layer's names in cookie which user don't want the results to be displayed.
			  Now loop through the query layers and check each layer against the results Layer array. If found, remove it
			*/
			if(rLayers)
			{
				qLayers.each(function(i)
				{
					if(jQuery.inArray(i, rLayers.split(','))==-1)
					{
						filteredArr.push(i);
					}
				});
			}
			else
			{
				filteredArr = qLayers;
			}
			
			//Do nothing if there is no layers
			if(filteredArr.length==0) return;
			
			//Start loader after the above IF condition
			this.mapElement.fire( 'astun:dataLoadBegin', {} );
			
			switch( queryType ) 
				{ // switch
					case 'shapeInfo' :
					{
						//shapeInfo
							var parameters = 
							{
									'MapSource': this.mapSource.mapName,
									'RequestType': 'GeoJSON',
									'ServiceAction': 'GetMultiInfoFromShape',
									'ActiveTool': 'MultiInfo',
									'ActiveLayer': '', //can be left blank?
									//'Layers': 'faults',
									'Layers': filteredArr, //always use current layers
									'mapid': -1,
									'axuid': new Date( ).valueOf( ),
									'Shape':coordinates
							};
							var thisPointer = this ;

							var queryOptions = { 'params': parameters, 'url':this.getMapService.uri }
							
							//Check if we need results per layer instead of getting all layers together
							if(astun.intranetApp){
									//Start with zero and increment if no results found. At the end if the number of this is equal to number of layers than no results found for any layer so show an alert
									astun.numResultsFoundForLayers = 0;
									astun.searchCancelled=false;
									isFirstTime = true; //To check if this is the first time the function is called in the loop
									serverErrorLayers = [] //List of layers for which server will timeout or return with error
									astun.mapWrapper.getResultsPerLayer(serverErrorLayers,isFirstTime,filteredArr.length,filteredArr,parameters,thisPointer,queryOptions,callbackFunction);									
							
							}
							else	{
								this.getMapService.request( 
									parameters, 
									function( json ) {
										if( json[0] && json[0].type && json[0].type == 'FeatureCollection' ) {
											callbackFunction( json, thisPointer, queryOptions);
										}
										else if( json.error ) {
											callbackFunction( { 'unexpectedResponse': json.error.response, 'error': json .error }, thisPointer ) ; 
										}
										else {
											callbackFunction( { 'unexpectedResponse': 'Unknown response', 'response': json }, thisPointer ) ; 
										
										}
										this.mapElement.fire( 'astun:dataLoadComplete', {} );
									}.bind( this )
								);
							}
							
							break;
					}
					case 'info':
						{ //info

							var parameters = 
							{
									//'MapSource': 'Elmbridge/Faults',
									'MapSource': this.mapSource.mapName,
									'RequestType': 'GeoJSON',
									'ServiceAction': 'GetMultiInfoFromPoint',
									'ActiveTool': 'MultiInfo',
									'ActiveLayer': '', //can be left blank?
									//'Layers': 'faults',
									'Layers': filteredArr, //always use current layers
									'Easting':	coordinates.lon,
									'Northing':	coordinates.lat,
									'mapid': -1,
									'axuid': new Date().valueOf(),
									//'tolerance': this.openlayers.getResolution( ) * 24 
									'tolerance': this.openlayers.getResolution( ) * pxTolerance 
							};
							var thisPointer = this,
								queryOptions = { 'params': parameters, 'url':this.getMapService.uri };
							
							//Check if we need results per layer instead of getting all layers together
							if(astun.intranetApp) {
									//Start with zero and increment if no results found. At the end if the number of this is equal to number of layers than no results found for any layer so show an alert
									astun.numResultsFoundForLayers = 0;
									astun.searchCancelled=false;
									isFirstTime = true; //To check if this is the first time the function is called in the loop
									serverErrorLayers = [] //List of layers for which server will timeout or return with error
									astun.mapWrapper.getResultsPerLayer(serverErrorLayers,isFirstTime,filteredArr.length,filteredArr,parameters,thisPointer,queryOptions,callbackFunction);
							}
							else {
								this.getMapService.request( 
									parameters, 
									function( json ) {
										if( json[0] && json[0].type && json[0].type == 'FeatureCollection' ) {
											callbackFunction( json, thisPointer, queryOptions);
										}
										else if( json.error ) {
											callbackFunction( { 'unexpectedResponse': json.error.response, 'error': json .error }, thisPointer ) ; 
										}
										else {
											callbackFunction( { 'unexpectedResponse': 'Unknown response', 'response': json }, thisPointer ) ; 
										
										}
										this.mapElement.fire( 'astun:dataLoadComplete', {} );
									}.bind( this )
								);
							}

							break;
							/*
							var successFunc = function( transport ){
								try 
								{
									var strResponse = transport.responseText ; 
									if( strResponse.search( /FeatureCollection/ ) > 0 )
									{
										var results = transport.responseText.evalJSON( );
										callbackFunction( results, thisPointer ) ;
									}
									else
									{
										callbackFunction( {'unexpectedResponse': transport.responseText}, thisPointer ) ; 
									}
								}
								catch( parsingError ) 
								{
									if( transport.status != 0 ) { // i.e. if not fail because interrupted
										alert( "Error: Invalid iShareMaps search page!" );
									}
								}
								finally {
									this.mapElement.fire( 'astun:dataLoadComplete', {} );
								}
								
							}
							new Ajax.Request
							( this.ismDataLayer.url, 
								{
									method: 'get',
									parameters: parameters,
									onFailure: function( transport ) 
									{
										alert( "Error: Failed to get iShareMaps search page!" );
									},
									onSuccess : successFunc.bindAsEventListener( this )		
								}
							 ); //Ajax request for infoClick and showMapToolTip
							break ;
							*/
						} //info									
			
				} // switch			
		}, //getMapMultiInfo
		'findMyNearest': function( location, layername, maxResults, distance, callbackFunction ) {
			/*
			 * Finds nearest items on map as GeoJSON collection and calls supplied function with the results
			 * location {<OpenLayers.LonLat>} - point around which to call query
			 * layername - name defined in map source
			 * maxResults - most results to return
			 * distance - furthest results can be returned from
			 * callbackFunction - function to call on successful query, should take two arguments( response, pointer to this map wrapper )
			 */		
			this.currentPosition = location;			
			var parameters = 
				{
					'ServiceAction': 'ShowMyClosest',
					'ActiveTool': 'MultiInfo',
					'ActiveLayer': layername,
					'Distance':  distance ,
					'MaxResults': maxResults,
					'Easting':	location.lon,
					'Northing':	location.lat	
				};
			this.getGeoJSONData(
				parameters,
			callbackFunction );
		
		}, //findMyNearest
		'findMy': function( location, layername, callbackFunction ) {
			/*
			 * Finds nearest items on map as GeoJSON collection and calls supplied function with the results
			 * location {<OpenLayers.LonLat>} - point around which to call query
			 * layername - name defined in map source
			 * maxResults - most results to return
			 * distance - furthest results can be returned from
			 * callbackFunction - function to call on successful query, should take two arguments( response, pointer to this map wrapper )
			 */		
			this.currentPosition = location;			
			this.getGeoJSONData( {
				'ServiceAction': 'ShowService',
				'ActiveTool': 'MultiInfo',
				'ActiveLayer': layername,
				'Easting':	location.lon,
				'Northing':	location.lat
			},
			callbackFunction );
		
		},

		'clearThemePopups': function( ) {
		/**
		* Function: clearThemePopups 
		* Removes removes all Theme Popups from the display and destroy them 
		*
		* Parameters:
		*
		* Returns:
		*/
			if( this.arThemePopup.length > 0 ) {
				while( this.arThemePopup.length )
				{
					var popup = this.arThemePopup.pop( );
					this.openlayers.removePopup( popup );
					popup.destroy( );
					popup = null;
				}
			}
		},
		
		
		'showLocation': function( en )
		{
		/**
		* Function: showLocation 
		* Draws a marker to correspond to a specific position
		*
		* Parameters:
		* en - {object ) OpenLayers.LonLat object
		*
		* Returns:
		*/
			
			//delete any previous marker and create new one
			this.deleteMarkers( );
			var image = 'images/addressiconsmall.gif';
			var size = new OpenLayers.Size( 14,22 );
			var offset = new OpenLayers.Pixel( -( size.w/2 ), -size.h );
			var icon = new OpenLayers.Icon( image, size, offset );
			this.createMarker( en, icon );
			
			
		},
		

		'deleteMarkers': function( )
		{
		/**
		* Function: deleteMarkers 
		* Deletes all markers currently visible and destroys them from marker layer
		*
		* Parameters:
		*
		* Returns:
		*/
		
			if( this.markerLayer ){
				for( var i=0; i<this.markerLayer.markers.length; i++ )
				{
					this.deleteMarker( this.markerLayer.markers[ i ] );
				}
			}
		},
		'showResults' : function( response, type, queryOptions ) 
		{//ShowFeatures
			if(response.unexpectedResponse) {
				var displayname = '';
				var htmlResponse = '' + response.unexpectedResponse;
			}
			else{ 
				var featureCollectionArray = response.slice();
				var featureCol = featureCollectionArray[0];	
				var html = '';
				this.clearPopups();	
				this.deleteMarker(this.currentLocation.marker);
				this.currentLocation.marker = null;
				var popupBounds = new OpenLayers.Bounds();
				popupBounds.extend(this.currentPosition);
				this.query.layer = featureCol.properties.layer;
				OpenLayers.Popup.FramedCloud.prototype.fixedRelativePosition = true;
				OpenLayers.Popup.FramedCloud.prototype.relativePosition = "tr"; 
				html += featureCol.properties.htmlHeader;
				var features = featureCol.features.slice();
				while ( features.length )
				{
					var feature = features.shift(); // 
					var popupHTML = '<h1>'+feature.id+'</h1>' ;
					popupHTML += '<div class="atPopupFeatureInfo">';
					popupHTML += feature.properties.html;
					popupHTML += '</div>';
										
					var lonLat = new OpenLayers.LonLat(feature.geometry.coordinates[0][0], feature.geometry.coordinates[0][1]);
					
					popupBounds.extend(lonLat);
					// if third coordinate different then dealing with bounding box, extend to that too, put marker in centre
					if ((feature.geometry.coordinates[0][0] !== feature.geometry.coordinates[2][0]) || (feature.geometry.coordinates[0][1] !== feature.geometry.coordinates[2][1])) {
						var topRight = new OpenLayers.LonLat(feature.geometry.coordinates[2][0], feature.geometry.coordinates[2][1]);
						popupBounds.extend(topRight);
						var featureBounds = new OpenLayers.Bounds();
						featureBounds.extend(lonLat);
						featureBounds.extend(topRight);
						lonLat = featureBounds.getCenterLonLat();
					}
					
					var featurePopup = new OpenLayers.Popup.FramedMarker
					(
						null, 
						lonLat, 
						new OpenLayers.Size(20, 18), 
						popupHTML, 
						{size: new OpenLayers.Size(1, 1), pixel: new OpenLayers.Pixel(0, 0)}, 
						false,  // no close box
						function( evt ){},
						astun.settings.baseURL+'css/jQuery/'+ astun.settings.themeName + '/images/popup-bg.png'
					);								
					this.query.popups.push(featurePopup);
					var temp = jQuery("<div>"+feature.properties.html+"<div>");
					var mapPinNum = "<p><strong>Map pin:</strong>" + feature.id + "</p>";
					temp.find('.infoResult').append(mapPinNum)
					html += temp.html(); //We want results HTML to be in ascending order of distance
					
				}		
				html += featureCol.properties.htmlFooter;
					
				this.openlayers.zoomToExtent(popupBounds.scale(1.4), true); //Add some padding and find zoom level as we will probably be using cached tiles
				
				
				var icon = new OpenLayers.Icon('images/zoomToIcon.gif', new OpenLayers.Size(34, 34), null, function(size) {
					return new OpenLayers.Pixel(-12, 2-size.h);
				});
				if( this.currentPosition ) {
					this.currentLocation.marker = this.createMarker(this.currentPosition, icon);
				}
				
				var numPopups = this.query.popups.length;
				for (var i = numPopups -1 ; i > -1; --i) {
					this.openlayers.addPopup(this.query.popups[i]); // Reverse order to give nearest resutls highest z-index.
				}
				var displayName = this.query.layer;
				var htmlResponse = html;
			}
			
			this.mapElement.fire("astun:resultsReceived", {'type': type, 'displayName': displayName, 'html': htmlResponse, 'json': response, 'queryOptions':queryOptions} );
				
		} ,
		//sfeatureName: name of polygon feature on layer		
		'zoomToFeature': function( sFeatureName )
		{
		/**
		* Function: zoomToFeature 
		* Zoom-in on polygon with the specified name to occupy the best maximum map view region
		*
		* Parameters:
		* sFeatureName - {string} matching polygon.attributes.name
		*
		* Returns:
		*/
			if( !this.thematicLayer ) {
				this.thematicLayer = $A( this.openlayers.layers ).find( function( layer ){
					return layer.CLASS_NAME == 'OpenLayers.Layer.Vector';
				} );
			}
			var oFeature = null;
			if( !this.thematicLayer )
				return ;

			for( var i=0; i< this.thematicLayer.features.length; i++ )
			{
				if( this.thematicLayer.features[ i ].attributes.name == sFeatureName )
				{
					oFeature = this.thematicLayer.features[ i ];
					break ;
				}
			}
			if( !oFeature )
				return

			var boundingBox = oFeature.geometry.getBounds( );
			this.openlayers.zoomToExtent( boundingBox,false );
		},
		'searchISMLayer': function( layer, field, value, callbackFunction ) {
			var parameters = 
				{
					'ServiceAction': 'SearchLayer',
					'ActiveTool': 'MultiInfo',
					'ActiveLayer': layer,
					'SearchLayer': layer,
					'SearchField': field,
					'SearchValue': value,
					'MapSource': this.mapSource.mapName
				};
			var thisPointer = this ;
			var queryOptions = {'params': parameters, 'url':this.getMapService.uri, 'isFirstResult':true}
			
			this.getGeoJSONData( 
				parameters,
				function( response, mapWrapper ) {
					if( response.unexpectedResponse ) {
						callbackFunction( { 'unexpectedResponse': 'Unknown response', 'response': response }, 'searchISM', queryOptions );
					}
					else {
						callbackFunction( response, 'searchISM', queryOptions );
					}
				}
			 );
		}
	});
})();
