/**
HubOnline Map Search
version 1.0

namespace: jQuery.hubMapSearch

functions:
 - init
 - pageData
 - populate
 - createMarker
 - focusListing
 - getData
 
private members:
 - Tooltip
 - HTMLControl
 
*/

;(function($){

	$.hubMapSearch = {};

	/**
	errorMessages:
	 - the global object for standard error messages
	*/
	$.hubMapSearch.errorMessages = {
		en : {
			functionDoesNotExist : "hubMapSearch Error 1: The function does not exist",
			addressNotFound: "This address cannot be found.  Please modify your search.",
			browserNotCompatible: "This browser is reported as being not compatible with Google Maps.",
			cannotLoad: "Cannot load the Google Maps API at this time.  Please check your connection."
		}
	};
	
	/**
	defaultOptions:
	 - override by passing an anon object to the map constructor
	*/
	$.hubMapSearch.defaultOptions = {
		// Initial type of map to display
		language: "en",
		// mapType options: "map", "sat", "hybrid"
		mapType: "map",
		// default map center is Australia
		mapCenter: [-25.274395,133.775136],
		mapDimensions: [600, 600],
		mapZoom: 3,
		// mapControlSize options: "large", "small"	
		mapControlSize: "large",		

		// GMap settings
		mapEnableType: true,
		mapEnableOverview: false,
		mapEnableDragging: true,
		mapEnableInfoWindows: true,
		mapEnableDoubleClickZoom: false,
		mapEnableScrollZoom: false,
		mapEnableSmoothZoom: false,
		mapEnableGoogleBar: false,
		mapEnableScaleControl: false,
		mapShowjMapIcon: true,

		// Map Search settings
		mapShowPaginationControl: true,
		mapShowResultControl: true,
		agentID: 0,
		iconImagePath: '/img/gmaps/markers/',
		agentsFolderPath: 'http://media.hubonline.com.au/agents_folder/',
		resultsPerPage: 50,
		xslID: 2045,
		f_ct: '1',
		f_ps: '2',
		
		// Form Defaults
		saleType: 1
	};

	/**
	init():
			load the search form, options and pre-map state
			generate a hidden map
			load the data and display a map once a search query is submitted
	*/
	$.hubMapSearch.init = function(el, options, callback) {
		var options = $.extend({}, $.hubMapSearch.defaultOptions, options);
		var options = this.options = $.meta ? $.extend({}, options, $(this).data()) : options;
			
		// TODO
		// check for any other instances of the map and bail, or at least until there's sufficient encapsulation
			
		// check for GMap compatibility  
        
		if (typeof google.maps.BrowserIsCompatible == 'undefined') {
			$(el).text(this.errorMessages[options.language].cannotLoad).css({
				color: "#f00"
			});
			throw Error(this.errorMessages[options.language].cannotLoad);
		}
        
		if (!google.maps.BrowserIsCompatible()) {
			$(el).text(this.errorMessages[options.language].browserNotCompatible).css({color: "#f00"});
			throw Error(this.errorMessages[options.language].browserNotCompatible);
		}
        
    // initialise the GMap2 object
        
		el.hubMapSearch = this.GMap2 = new google.maps.Map2(el);
        
    // cleanup crew to window.unload please
        
		$(window).unload(function(){
				GUnload();
		});
        
    switch(options.mapType) {
			case "map":
				var mapType = G_NORMAL_MAP;
			break;
			case "sat":
				var mapType = G_SATELLITE_MAP;
			break;
			case "hybrid":
				var mapType = G_HYBRID_MAP;
			break;
		}
        
		el.hubMapSearch.setCenter(
			new google.maps.LatLng(options.mapCenter[0], options.mapCenter[1]), 
			options.mapZoom, 
			mapType
    );
        
    $.hubMapSearch.markerManager = new MarkerManager($.hubMapSearch.GMap2);
        
    // Set map options
		var mapControls = el.hubMapSearch.getDefaultUI();
				mapControls.zoom.scrollwheel = false;
		switch (this.options.mapControlSize) {
			case "small":
				mapControls.controls.smallzoomcontrol3d = true;
				mapControls.controls.largemapcontrol3d = false;
				mapControls.controls.scalecontrol = false;
				break;
			case "large":
				mapControls.controls.smallzoomcontrol3d = false;
				mapControls.controls.largemapcontrol3d = true;
				break;
		}
		el.hubMapSearch.setUI(mapControls);
		
		if (options.mapEnableType) {
			el.hubMapSearch.addControl(new google.maps.MapTypeControl()); 
		}
		
		if (options.mapEnableOverview) { 
			el.hubMapSearch.addControl(new google.maps.OverviewMapControl());
		}
		
		if (!options.mapEnableDragging) {
			el.hubMapSearch.disableDragging(); 
		}
		
		if (!options.mapEnableInfoWindows) {
			el.hubMapSearch.disableInfoWindow(); 
		}
		
		if (options.mapEnableDoubleClickZoom) {
			el.hubMapSearch.enableDoubleClickZoom(); 
		}
		
		if (options.mapEnableScrollZoom) {
			el.hubMapSearch.enableScrollWheelZoom();
		}
		
		if (options.mapEnableSmoothZoom) {
			el.hubMapSearch.enableContinuousZoom();
    }
		
		if (options.mapEnableGoogleBar) {
			el.hubMapSearch.enableGoogleBar();
		}
		
		if (options.mapEnableScaleControl) {
			el.hubMapSearch.addControl(new google.maps.ScaleControl());
		}
    
    // load data, paginate, populate map/table/controls
    
		this.mapElement = el;
		//$.hubMapSearch.listings = options.listings;
		this.pages = [];
		this.tables = [];
		this.pageBounds = []
		this.listingTable = "";
		this.searchForm = "";
		this.searchFormAdvanced = "";
		this.searchFormAdvancedLink = "";
		this.defaultSearch = "";
		this.currentPage = 0;
		this.currentListing = null;
		this.activeMarker = null;
		this.tooltip = new Tooltip(el);
		this.sidebar = $('#gmapsidebar');
		this.flashMessage = new FlashMessage(el);
		this.lastAjaxQuery = null;
		
		this.mapResultControl = new HTMLControl({
				visible: false,
				selectable: true,
				printable: false
		});
        
    el.hubMapSearch.addControl(this.mapResultControl, new google.maps.ControlPosition(G_ANCHOR_TOP_RIGHT, new google.maps.Size(6, 30)));
        
		this.mapPagerControl = new HTMLControl({
				visible: false,
				selectable: true,
				printable: false
		});
        
		el.hubMapSearch.addControl(this.mapPagerControl, new google.maps.ControlPosition(G_ANCHOR_TOP_RIGHT, new google.maps.Size(6, 55)));
    
		// Hid Results Table by Default
		$('#resultsTable').hide();
		
		// Add PREV / NEXT Buttons
		var pagerButtons = '<div class="pager">' + 
				'<a href="#" class="prev disabled">Prev</a> | ' + 
				'<a href="#" class="next">Next</a>' + 
				'</div>'
		var mapResultsTable = $("#mapResultsTable");
				
		this.mapPagerControl.setText(pagerButtons);
		
		mapResultsTable.after(pagerButtons);
		mapResultsTable.before(pagerButtons);

		$('div.pager .prev').click(function(){
				$.hubMapSearch.changePage('prev');
				return false;
		});
				
		$('div.pager .next').click(function(){
				$.hubMapSearch.changePage('next');
				return false;
		});
		
		$('#gmapwrapper .loading').append('<div id="mapLoader">' + 
				'<p>Loading . . .</p>' +
				'<img src="/img/gmaps/loading.gif" />' +
				'</div>')
				.end()
				.find('#mapLoader')
				.hide();
		
		// Dragend: Hide tooltip
		google.maps.Event.addListener(this.GMap2, "dragend", function() {
			// Hide any tooltips
			var mapTooltip = $('#mapTooltip');
			if(mapTooltip.is(":visible")) {
				mapTooltip.css('visibility', 'hidden');
			}
		});
        
		// Setup Filter Form
        
		if (options.searchForm) {
			this.searchForm = $(options.searchForm);

			// Form Submit
			this.searchForm.submit(function(){
			
				// Remove Instructions Panel
				$('#mapInstructions').fadeOut('slow');
				
				var $this = $(this);
				var submitbutton = $this.find(':submit');
				submitbutton.attr('value', 'Loading...').attr('disabled', 'disabled');
				var query = $this.serialize();
				$.hubMapSearch.getData(query, function(){
						var button = submitbutton;
						button.attr('value', 'Search').removeAttr('disabled');
				});
				return false;
			});
		}
       
		// Setup Listing Table
		if (options.listingTable) {
			this.listingTable = $(options.listingTable);
			this.listingTable.click(function(e){
				var recordNumber = $(e.target).parent().attr('class').replace(/ highlight/, '');
				$.hubMapSearch.focusListing(recordNumber, true);
				// Hide any toolTips
				var mapTooltip = $('#mapTooltip');
				if(mapTooltip.is(":visible")) {
					mapTooltip.css('visibility', 'hidden');
				}
			});
		}
		
		// Run Default Search	(if any)	
		if (options.defaultSearch) {
			var query = options.defaultSearch.replace(/^[&|?]/g,''); // Remove any "&" or "?" characters that someone may have put at the start of the string...
			$.hubMapSearch.getData(query, function(){
				// empty callback
			});
		}
     
    if (typeof callback == 'function') return callback(el, options);
  }
    
	/**
	pageData():
			Chunks listings into arrays of X GMarkers for pagination, contents of optional table as strings 
	*/
	$.hubMapSearch.pageData = function() {
		var listings = this.listings;
        
		// pageData creates new state, so set currentPage back to 0
		this.currentPage = 0;
		this.currentListing = null;
		this.activeMarker = null;
		
		for (var i in listings) {
			if (i % this.options.resultsPerPage == 0) {
				pagenumber = i / this.options.resultsPerPage;
				this.pages[pagenumber] = [];
				this.tables[pagenumber] = "";
				this.pageBounds[pagenumber] = new google.maps.LatLngBounds();
			}
			
			var marker = this.createMarker(listings[i], i);
			
			this.pages[pagenumber].push(marker);
			this.pageBounds[pagenumber].extend(marker.getLatLng());
			
			// Table Values
			var displayPrice = (listings[i].displayprice.length === 0) ? "-" : listings[i].displayprice;
			var displayBed = (listings[i].bed.length === 0) ? "-" : listings[i].bed;
			var displayBath = (listings[i].bath.length === 0) ? "-" : listings[i].bath;
			var displayCar = (listings[i].parking.length === 0) ? "-" : listings[i].parking;
			
			// Find Correct Icon
			var iconImagePath = this.options.iconImagePath;
			var iconDisplay = "";
			switch (listings[i].categoryID) {
				case 1: 
					iconDisplay = iconImagePath + "marker-residential-table.gif";
					break;
				case 2:
					iconDisplay = iconImagePath + "marker-commercial-table.gif";
					break;
				case 3: 
					iconDisplay = iconImagePath + "marker-residential-table.gif";
					break;
				case 4:	
				case 10:
					iconDisplay = iconImagePath + "marker-land-table.gif";
					break;
				case 5:			
					iconDisplay = iconImagePath + "marker-rural-table.gif";
					break;
				case 6:			
					iconDisplay = iconImagePath + "marker-propertyoverview-table.gif";
					break;
				default:
					iconDisplay = iconImagePath + "marker-residential-table.gif";
					break;
			}
			
			var tablerow = '<tr class="' + i + '">';
			tablerow += '<td>' + listings[i].streetno + ' ' + listings[i].address + ', ' + listings[i].suburb + '</td>';
			tablerow += '<td>' + displayPrice + '</td>';
			if(listings[i].categoryID != 4 && listings[i].categoryID != 10) {
				tablerow += '<td>' + displayBed + '</td>';
				tablerow += '<td>' + displayBath + '</td>';
				tablerow += '<td>' + displayCar + '</td>';
			} else {
				tablerow += '<td>-</td>';
				tablerow += '<td>-</td>';
				tablerow += '<td>-</td>';
			}
			tablerow += '<td>' + listings[i].propertytype + '</td>';
			tablerow += '<td><img src="' + iconDisplay + '" alt="' + listings[i].propertytype + '" /></td>';
			tablerow += '</tr>';
			
			this.tables[pagenumber] += tablerow;
		}
  }
    
	/**
	populate():
	Populate the map, table, HTMLControls
	check for pre-populated areas and clear
	*/
	$.hubMapSearch.populate = function(pageNumber) {
    
		var pageBounds = this.pageBounds[pageNumber];
    
    var newZoom = this.GMap2.getBoundsZoomLevel(pageBounds);
        
		// zoom level closer than 15 is too much, so make 15 the minimum;
		// generally, a zoom level 1 less than the bounds will keep markers from displaying
		// too close to the map edge
		if (newZoom > 15) {
			newZoom = 15;
		} else if (newZoom <= 15 && newZoom > 9) {
			newZoom = newZoom - 1;
		}

		this.markerManager.clearMarkers();
		this.GMap2.setCenter(pageBounds.getCenter(), newZoom);
		this.markerManager.addMarkers(this.pages[pageNumber], 0);
		this.markerManager.refresh();
        
		if (this.options.listingTable) {
				this.listingTable.html(this.tables[pageNumber]);
		}

    //$.hubMapSearch.focusListing(pageNumber * $.hubMapSearch.options.resultsPerPage);
        
		var resultText_propNumbers = (pageNumber * this.options.resultsPerPage + 1) + 
				' to ' + ((pageNumber + 1) * this.pages[pageNumber].length);
        
    var resultsText = resultText_propNumbers + ' of ' + this.listings.length + ' properties displayed';
        
    this.mapResultControl.setText('<div id="result">' + resultsText + '</div>'); 	
        
		if (this.listings.length >= this.options.resultsPerPage){
			this.mapPagerControl.setVisible(true);
		} else {
			this.mapPagerControl.setVisible(false);
		}
    
    var pager = $("div.pager");
    
		if(this.pages.length === 1) {
			pager.css('display','none');
		}
		
		if (pageNumber == 0) {
			$('div.pager .prev').addClass('disabled');
		} else if (pageNumber == (this.pages.length - 1)) {
			$('div.pager .next').addClass('disabled');
		} else {
			$('a', pager).removeClass('disabled');
		} 
  }
    
	/**
	changePage():
	Change the page to be displayed
	*/
	$.hubMapSearch.changePage = function(direction) {
		if (direction == 'next' && this.currentPage != (this.pages.length - 1)) {
			var nextpage = this.currentPage + 1;
		} else if (direction == 'prev' && this.currentPage != 0) {
			var nextpage = this.currentPage - 1;
		} else {
			var nextpage = null
		}
		
		if (nextpage != null) {
			//console.log('page: ' + $.hubMapSearch.currentPage + ' next: ' + nextpage);
			this.currentPage = nextpage;
			this.populate(nextpage);
		}
	}
    
	/**
	createMarker():
	 - Create & return Gmarker
	 - Bind tooltip event
	*/
  $.hubMapSearch.createMarker = function(listing, recordNumber) {
		var markerOptions = {
			pointLatLng: [],
			customIcon: true,
			customWindow: true,
			showTooltip: true,
			pointHTML: null,
			pointOpenHTMLEvent: "click",
			pointIsDraggable: false,
			pointIsRemovable: false,
			pointRemoveEvent: "dblclick",
			pointMinZoom: 4,
			pointMaxZoom: 17,
			pointIcon: null,
			pointMaxContent: null,
			pointMaxTitle: null
		}
    
		markerOptions.icon = new google.maps.Icon();
		
		var iconImagePath = this.options.iconImagePath;
    
    switch (listing.categoryID) {
    	case 1: 
        markerOptions.icon.image = iconImagePath + "marker-residential.gif";
    		break;
    	case 2:
    		markerOptions.icon.image = iconImagePath + "marker-commercial.gif";
    		break;
    	case 3: 
    		markerOptions.icon.image = iconImagePath + "marker-residential.gif";
    		break;
    	case 4:	
			case 10:
        markerOptions.icon.image = iconImagePath + "marker-land.gif";
    		break;
    	case 5:			
        markerOptions.icon.image = iconImagePath + "marker-rural.gif";
    		break;
			case 6:			
        markerOptions.icon.image = iconImagePath + "marker-propertyoverview.gif";
    		break;
			default:
				markerOptions.icon.image = iconImagePath + "marker-residential.gif";
				break;
    }
        
		markerOptions.icon.iconSize = new google.maps.Size(18, 25);
		markerOptions.icon.iconAnchor = new google.maps.Point(3, 24);
        
    var point = new google.maps.LatLng(listing.latitude, listing.longitude);
		var marker = new google.maps.Marker(point, markerOptions);
    
    marker.realImage = markerOptions.icon.image;
		
    this.tooltip.addTooltip(marker, {bed: listing.bed, bath: listing.bath, car: listing.parking, category: listing.categoryID, landSize: listing.landsize, landSizeUnits: listing.landsizeunits});
        
    marker.recordNumber = recordNumber;
    google.maps.Event.addListener(marker, 'click', function(){$.hubMapSearch.focusListing(this.recordNumber)});        
      
    return marker; 
  }
		
	/** 
	focusListing():
	 - Focus on a listing
	 - Highlight marker, highlight table row, display details in sidebar
	 - Callback from event listeners on the GMarkers and the optional results table event delegator
	 - panMap: bool; if true, calls the map panTo method and centers the map on the specified marker 
	*/
	$.hubMapSearch.focusListing = function (recordNumber, panMap) {
     
    var panMap = panMap || false;
    var marker = this.pages[this.currentPage][recordNumber % this.options.resultsPerPage];
    var data = this.listings[recordNumber];
    
    if (this.listingTable) {
      // get the new row
      var row = $('.' + recordNumber, this.listingTable);
      // un-highlight the old table row
      $('.' + this.currentListing, this.listingTable).removeClass('highlight');
    }
     
		if (this.activeMarker) {
			// un-set the old active marker icon
			this.activeMarker.getIcon().image = this.activeMarker.realImage;
		}
    
		this.activeMarker = marker;
		marker.getIcon().image = this.options.iconImagePath + "marker-active.gif";
		this.markerManager.refresh();
   		
		if (panMap) {
			this.GMap2.panTo(marker.getLatLng());
		}
        
    // stash the new current listing
    this.currentListing = recordNumber;
    
    // display details in sidebard
    var sidebar = this.sidebar;
    $('#sidebar_mainheading', sidebar).html(data.mainheading);
    $('#sidebar_price', sidebar).html(data.displayprice);
	
		var xslID = this.options.xslID
		
		$('#sidebar_image', sidebar).attr('src', this.options.agentsFolderPath + data.agentID + '/' + data.mainimage.replace('_lg','_sm'))
		.click(function() {
			window.location = '/pol/property/search.asp?f_PropertyID=' + data.pid + '&xsl=' + xslID;
		})
		.show();
		
		$('#sidebar_address', sidebar).html(data.streetno + ' - ' + data.address + ', ' + data.suburb);
		
		// Display Bed, Bath & Car Icons
		// Note: Don't display Bed & Bath icons if its a Commercial listing
		// Note: Don't Display ANY icons if it's a Land Listing
		
		if(data.categoryID !== 4 && data.categoryID !== 10) {
			$('#sidebar_landSize').hide();
			$('#sidebar_features').show();
			if(data.categoryID !== 2) {
				$('#sidebar_featurebed', sidebar).html('<img src="/img/gmaps/bed_gmap.gif" alt="Number of bedrooms" />' + data.bed);
				$('#sidebar_featurebath', sidebar).html('<img src="/img/gmaps/bath_gmap.gif" alt="Number of bathrooms" />' + data.bath);
			}
			$('#sidebar_featurecar', sidebar).html('<img src="/img/gmaps/car_gmap.gif" alt="Number of car spaces" />' + data.parking);
		} else if(parseInt(data.landsize) > 0) {
			$('#sidebar_features').hide();
			$('#sidebar_landSize').show();
			$('#sidebar_landSize', sidebar).html('<span>Land Size: </span>' + data.landsize + ' ' + data.landsizeunits);
		}
		
		// display Property Description
		$('#sidebar_description', sidebar).html(data.description.substring(0,150) + '... ' + '<a href="/pol/property/search.asp?f_PropertyID=' + data.pid + '&xsl=' + this.options.xslID + '">more details &#187;</a>');
		
    // highlight marker - change icon?
        
    // highlight the table row
    if (this.listingTable) {
			row.addClass('highlight');
    }
	
		document.location = "#content";
  }
    
	/**
	getData():
	 - wrapper for an $.ajax async request
	 - callback is a function to call once processing the response, as either a success
			or error, is complete; e.g. removing a 'loading' message
	*/
	$.hubMapSearch.getData = function(query, callback) {
		var getQuery = ((this.options.hasOwnProperty('headOfficeID')) ? 'f_HeadOfficeID=' + this.options.headOfficeID : 'f_AgentID=' + this.options.agentID) + '&' + query + '&f_ct=' + this.options.f_ct + '&f_ps=' + this.options.f_ps;
		var handlerCallback = callback;
				
		// we don't want the user to continually tap the Search button and thump the server with ajax requests,
		// so only allow an ajax query if the new query params are different from the last successful query
		if (this.lastAjaxQuery == null || this.lastAjaxQuery != getQuery) {
			// Show Loading Panel
			$('#mapLoader').fadeIn('slow');
		
			this.flashMessage.clear();
				
			$.ajax({
				type: 'GET',
				url: '/_lib/googleMapSearch/mapsearch_ajax.asp',
				data: getQuery,
				dataType: 'json',
				success: function(json, status){					
					$.hubMapSearch.getDataSuccess(json, handlerCallback);
					$.hubMapSearch.lastAjaxQuery = getQuery;
				},
				error: function(XMLHttpRequest, status, errorThrown){
					$.hubMapSearch.getDataError(status, handlerCallback);
					$.hubMapSearch.lastAjaxQuery = null;
				}
			});
		} else {
			if ($.isFunction(handlerCallback)) handlerCallback();
		}
	}
    
	/**
	getDataSuccess()
	 - handle the ajax response data and call the display functions
	 - callback: see getData()
	*/
	$.hubMapSearch.getDataSuccess = function(json, callback) 
	{
			
			var callback = callback || '';
			var options = this.options;
			var oldListings = typeof this.listings;
			var newListings = json.listings;
			
			// Hide Loading Panel
			$('#mapLoader').fadeOut('slow');
			$('#resultsTable').show();
			
			// populate the new data
			if (newListings.length > 0) {
					this.listings = newListings;
					this.pageData();
					this.populate(0);
			} else {
					this.getDataError('noresults');
			}
			
			// test for old data; if oldListings is empty, the map isn't displaying, so show it
			if (oldListings == 'undefined') {
					this.mapResultControl.setVisible(true);
					// display map
			}
			
			if ($.isFunction(callback)) callback();
			
	}
    
	/**
	getDataError()
	 - handle the ajax response error and do something useful
	 - callback: see getData()
	*/
	$.hubMapSearch.getDataError = function(status, callback) {
    var callback = callback || '';
    var message = '';
    
		// Hide Loading Panel
		$('#mapLoader').fadeOut('slow');
		
		// possible jQuery values for status: 'timeout', 'error', 'notmodified', 'parsererror'
		// custom error values for status: 'noresults' = search returned no properties
        
		switch (status) {
			case 'noresults':
				message = 'No properties with map data have been found, please use the filter to widen your search criteria.';
				break;
			case 'timeout':
				message = 'The last search could not complete in time. Please try again.';
				break;
			case 'error':
				message = 'There was an error.'
				break;
			default:
				message = 'Oh dear, something went wrong: ' + status;
				break;
		}
		
		this.flashMessage.show('error', message);
		$('#resultsTable').hide();
		this.markerManager.clearMarkers();
		
		if ($.isFunction(callback)) {
			callback();
		}
  }
    
	/**
	flashMessage()
	 - flash a message box above the map div
	 - requires p#flashMessage to be created w/ display:none in hubMapSearch.init()
	*/
	var FlashMessage = function(anchor) {
			this.anchor = anchor;
			this.messageElement = $('<p id="flashMessage" style="display:none;"/>').insertBefore(anchor);
			this.messageType = '';
	}
    
  FlashMessage.prototype = {
        
		show: function(type, message) {
			if (this.messageType != '') this.messageElement.removeClass(this.messageType);
			this.messageType = type;
			this.messageElement.addClass(type).html(message).slideDown('fast');
		},
		
		clear: function() {
			this.messageElement.slideUp('fast');
		}
  }
    
	/**
	HTMLControl:
	 - private constructor
	 - inherits from GMap's GControl class
	 - creates custom control boxes on the map
  */
	var HTMLControl = function(options) {				
		this.isVisible = true;
		this._isPrintable = false;	
		this._isSelectable = false;
        
		if (options) {
			this.isVisible = (options.visible === false) ? false : true;
			this._isPrintable = (options.printable === true) ? true : false;
			this._isSelectable = (options.selectable === true) ? true : false;
		}
		
    this.setVisible = function($bool) {
			this._div.style.display = ($bool) ? 'block' : 'none';
			this.isVisible = $bool;
		}
			
		this.setVisible = function($bool) {
			this._div.style.display = ($bool) ? 'block' : 'none';
			this.isVisible = $bool;
		}
  }	
	
	HTMLControl.prototype = new google.maps.Control();
	
	HTMLControl.prototype.initialize = function(el) {
    this.selectable = function() {
			return this._isSelectable;
		};
		
		this.printable = function() {
			return this._isPrintable;
		};
		
    this._div = document.createElement('div');			
		this.setVisible(this.isVisible);
		el.getContainer().appendChild(this._div);

    return this._div;
  }
	
	HTMLControl.prototype.setText = function(html) {
		this._div.innerHTML=html;
	}
	
	HTMLControl.prototype.setBorderColor = function(bordercolor) {
		this._div.style.border="1px solid" + bordercolor;
	}
	
	HTMLControl.prototype.getDefaultPosition = function() {
		return new google.maps.ControlPosition(G_ANCHOR_TOP_LEFT, new google.maps.Size(7,7));
	};
	
	/**
	Tooltip
	 - private constructor
	 - creates a p element at the location of the mouseovered marker
	 - destroys the p element on mouseout
	 - showTooltip() and hideTooltip() should be called from a marker's event listener
	*/
  var Tooltip = function(mapContainer) {
		var tooltipContent = '<p id="mapTooltip" style="position:absolute;visibility:hidden">' + 
			'<span id="mapTooltip_bed"/><img id="mapTooltip_bedIcon" src="/img/gmaps/tooltip-bed.gif" />' +
			'<span id="mapTooltip_bath"/><img id="mapTooltip_bathIcon" src="/img/gmaps/tooltip-bath.gif" />' +
			'<span id="mapTooltip_car"/><img id="mapTooltip_carIcon" src="/img/gmaps/tooltip-car.gif" />' +
			'<span id="mapTooltip_landSize"/></p>'
		this.mapContainer = mapContainer;
		this.tooltipElement = $(tooltipContent).appendTo(mapContainer)
  };
    
  Tooltip.prototype = {
    addTooltip: function(marker, data) {
			marker.tooltip = data;
			marker.tooltipRef = this.tooltipElement;

			google.maps.Event.addListener(marker, "mouseover", this.showTooltip);
			google.maps.Event.addListener(marker, "mouseout", this.hideTooltip);
    },
        
    showTooltip: function() {
			// 'this' refers to the marker bound in the GEvent.addListener (see: Tooltip.prototype.addTooltip)
			
			var tooltip = this.tooltipRef;
			
			var bedAmount = (typeof this.tooltip.bed === "undefined") ? 0 : this.tooltip.bed;
			var bathAmount = (typeof this.tooltip.bath === "undefined") ? 0 : this.tooltip.bath;
			var carAmount = (typeof this.tooltip.car === "undefined") ? 0 : this.tooltip.car;
			var landSize = (typeof this.tooltip.landSize === "undefined" || this.tooltip.landSize === null) ? "N/A" : this.tooltip.landSize + " " + this.tooltip.landSizeUnits;
			
			// Don't display Bed & Bath icons if its a Commercial listing
			if(this.tooltip.category === 2) {
				// Hide Bed Icon
				tooltip.find('#mapTooltip_bed').css('display', 'none');
				tooltip.find('#mapTooltip_bedIcon').css('display', 'none');
				// Hide Bath Icon
				tooltip.find('#mapTooltip_bath').css('display', 'none');
				tooltip.find('#mapTooltip_bathIcon').css('display', 'none');
				// Show Car Icon
				tooltip.find('#mapTooltip_car').html('' + carAmount + '').css('display', 'inline');
				// Hide Land Size
				tooltip.find('#mapTooltip_landSize').css('display', 'none');
			} else if(this.tooltip.category === 4 || this.tooltip.category === 10) {
				// Hide Bed Icon
				tooltip.find('#mapTooltip_bed').css('display', 'none');
				tooltip.find('#mapTooltip_bedIcon').css('display', 'none');
				// Hide Bath Icon
				tooltip.find('#mapTooltip_bath').css('display', 'none');
				tooltip.find('#mapTooltip_bathIcon').css('display', 'none');
				// Hide Car Icon
				tooltip.find('#mapTooltip_car').css('display', 'none');
				tooltip.find('#mapTooltip_carIcon').css('display', 'none');
				// Show Land Size
				tooltip.find('#mapTooltip_landSize').html('Land size: ' + landSize + '').css('display', 'inline');
			} else {
				// Show Bed Icon
				tooltip.find('#mapTooltip_bed').html('' + bedAmount + '').css('display', 'inline');
				tooltip.find('#mapTooltip_bedIcon').css('display', 'inline');
				// Show Bath Icon
				tooltip.find('#mapTooltip_bath').html('' + bathAmount + '').css('display', 'inline');
				tooltip.find('#mapTooltip_bathIcon').css('display', 'inline');
				// Show Car Icon
				tooltip.find('#mapTooltip_car').html('' + carAmount + '').css('display', 'inline');
				// Hide Land Size
				tooltip.find('#mapTooltip_landSize').css('display', 'none');
			}

			var markerlatlng = this.getLatLng();
			var iconsize = this.getIcon().iconSize;
			
			var mapEdgeLeft = 0;
			var mapEdgeRight = $.hubMapSearch.GMap2.getContainer().offsetWidth;
			var mapEdgeTop = 0;
			
			var markerX = $.hubMapSearch.GMap2.fromLatLngToContainerPixel(markerlatlng).x;
			var markerY = $.hubMapSearch.GMap2.fromLatLngToContainerPixel(markerlatlng).y;
			
			// 2px buffer around the tooltip's offsetWidth
			var tooltipWidth = tooltip.outerWidth() + 2;
			
			// center the tooltip over the map point
			var tooltipOffsetX = tooltipWidth / 2;
			
			// give it 3px of space above the point
			var tooltipOffsetY = tooltip.height() + 3;
			
			var targetX = markerX - tooltipOffsetX;
			var targetY = markerY - (tooltipOffsetY + iconsize.height);
			
			// check map edge left and offset if needed
			if (targetX <= mapEdgeLeft) {
					targetX = targetX + (mapEdgeLeft - targetX) + 10;
			} else if ((targetX + tooltipWidth) >= mapEdgeRight) {
					targetX = targetX - (targetX + tooltipWidth - mapEdgeRight) - 10;
			}
			
			// check map edge top and offset below icon if needed
			if (targetY <= mapEdgeTop) {
				targetY = markerY + 3;
			}
			
			tooltip.css('left', targetX + 'px')
				.css('top', targetY + 'px')
				.css('visibility', 'visible');
    },
        
    hideTooltip: function() {
      // 'this' refers to the marker bound in the GEvent.addListener (see: Tooltip.prototype.addTooltip)
            
      var tooltip = this.tooltipRef;
      tooltip.css('visibility', 'hidden');
    }
  };

	/**
	jQuery(expr).hubMapSearch(options)
	 - bind the sucker to the jQuery object and off we go
	*/
	$.fn.hubMapSearch = function(options, callback) {
		return this.each(function(){
			$.hubMapSearch.init(this, options, callback);
		}); 
	} 
})(jQuery);
