Element.extend({
    ancestors: function () {
        var result = [], el = this;
        while (el = el.getTag() !== 'html' ? el.getParent() : false) result.push(el);
        return $$(result);
    }
});

MAX_ZOOM = 5;
function restrictZoom(oldZoom, newZoom) {
	if (newZoom < MAX_ZOOM) {
		this.setZoom(MAX_ZOOM);
		alert('You cannot zoom out any further.');
	}
}

NE_LIMIT = [61, 0];
SW_LIMIT = [50, -10];
function restrictPan() {
	var c = this.getCenter();
	var lat = c.lat();
	var lng = c.lng();
	if (lat > NE_LIMIT[0] || lat < SW_LIMIT[0] || lng > NE_LIMIT[1] || lng < SW_LIMIT[1]) {
		if (lat > NE_LIMIT[0]) lat = NE_LIMIT[0];
		if (lat < SW_LIMIT[0]) lat = SW_LIMIT[0];
		if (lng > NE_LIMIT[1]) lng = NE_LIMIT[1];
		if (lng < SW_LIMIT[1]) lng = SW_LIMIT[1]; 
		this.setCenter(new GLatLng(lat, lng));
	}
}

function initDefaultFieldValue(element, defaultValue) {
	element = $(element);
	if (element == null) return;
	if (element.value == '') {
		element.value = defaultValue;
		element.setStyle('color', '#b3b3b3');
	}
	element.addEvent('focus', function(defaultValue) {
		if (this.value == defaultValue) {
			this.value = '';
			this.setStyle('color', '#000');
		}
	}.bind(element, defaultValue));
	
	var f = updateDefaultFieldValue.bind(element, defaultValue)
	element.addEvent('blur', f);
	f();
}

function updateDefaultFieldValue(defaultValue) {
	if (this.value == '' || this.value.toLowerCase() == defaultValue.toLowerCase()) {
		this.value = defaultValue;
	}
	if (this.value == defaultValue) {
		this.setStyle('color', '#ccc');
	}
}

function convertMonetaryAmount() {
	var value = this.value;
	value = value.replace(/[\s,]/g, '')
	value = value.replace(/^(\d+)k$/i, function() { return arguments[1] * 1000 })
	value = value.replace(/^(\d+.?\d*)m$/i, function() { return arguments[1] * 1000000 })
	this.value = intcomma(value);	
}

function intcomma(value) {
	value = '' + value;
    var newValue = value.replace(/^(\d+)(\d{3})/, '$1,$2');
    if (value == newValue) {
        return newValue
    } else {
        return intcomma(newValue)
    }
}



function updateWheres() {

	var specifiers = [
		'location',
		'list',
		'map'
		];


	var mode = 0;

	if($('where_specifier_search')) {
		specifiers.every(function(value, index) {
			if ($('where_specifier_search').value == value) {
				mode = index;
				return false;
			} else {
				return true;
			}
		});
	}
	
	
	specifiers.each(function(value, index) {
		var tab = $('where_specifier_tab_' + specifiers[index]);
		if (tab) {
			if (index == mode) { 
				tab.addClass('active');
			} else {
				tab.removeClass('active');
			}
		}
	});
	
	var inputs = $ES('#where_specifiers .where_specifier');
	
	for (var i = 0; i < inputs.length; i++) {
		if (!inputs[i]) continue;
		if (i == mode) {
			inputs[i].setStyle('display', 'block');
		} else {
			inputs[i].setStyle('display', 'none');
		}
	}

	
}

function updateRentalAreas() {
	var region = $E('#where_specifier_list #field_region select');
	var areas = $E('#where_specifier_list #field_area');
	
	var current_area = region.options[region.selectedIndex].value;
	var new_areas = area_list[current_area];
	areas.empty();
	
	if (typeof new_areas != 'undefined') {
		new_areas.unshift('All areas in ' + current_area);
		
		var select = new Element('select');
		select.id = 'id_area';
		select.name = 'area';
		for (var i = 0; i < new_areas.length; i++) {
			var option = new Element('option');
			option.innerHTML = new_areas[i];
			option.value = (i == 0 ? '' : new_areas[i]);
			option.injectInside(select);
			if (new_areas[i] == initial_area) select.selectedIndex = i;
		}
		initial_area = '';
		select.injectInside(areas); 
			
		$E('#where_specifier_list #field_area').style.display = "block";
	} else {
		$E('#where_specifier_list #field_area').style.display = "none";
	}
	$E('#where_specifier_list .inputs').parentNode.style.height = "auto";
}

function initStylisedLabels(parent) {
	function toggleClass() {
		var label = this.getParent();
		if (label.nodeName != 'LABEL') {
			label = $E('label[for=' + this.id + ']', parent);
		}
		if (label) {
			if (this.checked) {
				this.addClass('checked');
				label.addClass('checked');
			} else {
				this.removeClass('checked');
				label.removeClass('checked');
			}
		}
	}
	
	$ES('input[type=checkbox]', parent).each(function(element) {
		element.addEvent('click', toggleClass);
		toggleClass.bind(element)();
	});
	
	function toggleRadioClass() {
		$$(this.form[this.name]).each(function(el) {
			toggleClass.bind(el)();
		});
	}
	$ES('input[type=radio]', parent).each(function(element) {
		element.addEvent('click', toggleRadioClass);
		toggleRadioClass.bind(element)();
	});
}


window.addEvent('domready', function() {
	var search = $('search');
	if (search && $E('form', search)) {
		
		// Area groups that are initially shown to the user is settable in the admin. These
		// groups will have a class of initially_shown
		var area_groups = $ES('#where_specifier_list div.search_area_group');
		
		if (area_groups) {
			for (var i = 0; i < area_groups.length; i++) {
				var area_group = area_groups[i];
				if (area_group.hasClass('initially_shown')) {
					continue;
				}
				
				var h3 = $E('h3', area_group);
				h3.appendText(' ');
				var show_hide_link = new Element('a', {'href': '#'}).injectInside(h3);
				var ul = $E('ul', area_group);
				ul.slide = new Fx.Slide(ul);
				// check if we have any checked boxes, if we do, the group needs to be
				// shown by default
				var check_boxes = $ES('input', area_group);
				var something_checked = false;
				if (check_boxes) {
					for (var j = 0; j < check_boxes.length; j++) {
						if (check_boxes[j].checked) {
							something_checked = true;
							break;
						}
					}
				}
				
				if (something_checked) {
					show_hide_link.innerHTML = '(hide)';
				}
				else {
					show_hide_link.innerHTML = '(show)';
					ul.addClass('hide');
					ul.slide.hide();
				}
				
				show_hide_link.addEvent('click', function (e) {
					e = new Event(e);
					$E('#where_specifier_list .inputs').getParent().setStyle('height', 'auto');
					var link = e.target;
					showHideAreas(link);
					e.stop();
				});
			}
			
			function showHideAreas(link) {
				var this_area_group = link.getParent().getParent();
				var ul = $E('ul', this_area_group);
				
				if (ul.hasClass('hide')) {
					// show has been clicked so show the areas
					ul.slide.slideIn();
					link.innerHTML = '(hide)';
					ul.removeClass('hide');
				}
				else {
					// hide has been clicked so hide the areas and 
					// untick the checkboxes
					ul.slide.slideOut();
					var check_boxes = $ES('input', ul);
					for (var i = 0; i < check_boxes.length; i++) {
						var check_box = check_boxes[i]
						check_box.checked = false;
						$(check_box.parentNode).removeClass('checked');
					}
					link.innerHTML = '(show)';
					ul.addClass('hide');
				}
			}
		}
		
		if ($('id_keywords')) {
			initDefaultFieldValue('id_keywords', 'Separate keywords / phrases with a comma');
		}
	
		if ($('id_min_price')) {
			initDefaultFieldValue('id_min_price', 'Any');
			initDefaultFieldValue('id_max_price', 'Any');
			initDefaultFieldValue('id_location', 'Anywhere');
			[$('id_min_price'), $('id_max_price')].filter(
				function(f) {return f != null}
			).map(
				function (f) {f.addEvent('blur', convertMonetaryAmount)}
			);
			[$('id_min_price'), $('id_max_price')].each(function(element) {
				convertMonetaryAmount.bind(element)();
			});
		}
	
		if (search.hasClass('property_search') || search.hasClass('rental_search') || search.hasClass('solicitor_search')) {
			
			new Element('input', {'name': 'where_specifier', 'type': 'hidden', 'id': 'where_specifier_search', 'value': selectedWhereSpecifier}).injectInside('where_tabs');
			
			var createTab = function(value, label, callback) {
				new Element('li', {'class': 'tab', 'id': 'where_specifier_tab_' + value}).adopt(
					new Element('a', {'href': '#', 'class': value})
						.setText(label)
						.addEvent('click', function(event) {
							$('where_specifier_search').value = value;
							updateWheres();
							if (callback) callback();
							new Event(event).stop();
						})
				).injectInside('where_tabs');
			}
			

			createTab('location', $E('#where_specifier_area label').getText());
			createTab('list', $E('#where_specifier_list label').getText());
			
			$E('#where_specifier_area input[type=radio]').remove();
			$E('#where_specifier_area label').remove();
			$E('#where_specifier_list input[type=radio]').remove();
			$E('#where_specifier_list label').remove();
			
			var mapParent = $('where_specifier_map');
			if(mapParent) {
				ms = new MapSearch(mapParent);
				createTab('map', 'Mark on a map', ms.loadMap.bind(ms));
				if ($('where_specifier_search').value == 'map') {
					ms.loadMap();
				}
			}
		}

		if (search.hasClass('solicitor_search')) {
			initDefaultFieldValue('id_location', 'Anywhere');
		}
		
		var regionSelect = $E('#where_specifier_list #field_region select');
		if (regionSelect) {
			regionSelect.addEvent('change', updateRentalAreas);
			updateRentalAreas();
		}
		
		updateWheres();

	}
	
	if ($('id_reference')) {
		initDefaultFieldValue('id_reference', 'Property ref. number');
	}
	
	// Rental email tracker
	$ES('.rental').each(function(el) {
		var propertyId = el.id.substring(16); // strip off 'rental_property_'
		el.getElements('a[href^=mailto:]').addEvent('click', function(event, propertyId) {
			new Ajax('/rentals/' + propertyId + '/log-email/').request();
		}.bindWithEvent(this, propertyId));
	});
	
	// List sorter hover effect
	sorters = $ES('.sorters a').extend($ES('#house_price_search_results thead a'));
	for (var i = 0; i < sorters.length; i++) {
		sorters[i].addEvent('mouseover', function () {
			$(this.parentNode).addClass('light');
			if ($(this.parentNode).hasClass('sorted')) {
				$(this.parentNode).addClass('sorted_light');
			}
			if ($(this.parentNode).hasClass('descending')) {
				$(this.parentNode).addClass('descending_light');
			}
		});
		sorters[i].addEvent('mouseout', function () {
			$(this.parentNode).removeClass('light');
			$(this.parentNode).removeClass('sorted_light');
			$(this.parentNode).removeClass('descending_light');
		});
	}
	
	$ES('#edit_saved_search_type, #advanced_search_property_type').each(function(element) {
		// For each type in list
		var type_categories = $ES('.type', element);
		for (var i = 0; i < type_categories.length; i++) {
			var type_select = $E('.type_select', type_categories[i]);
			type_select.types = $E('.field_wrapper', type_categories[i]);
			type_select.slide = new Fx.Slide(type_select.types);
			
			// Create "parent" checkbox
			type_select.input = new Element('input', {
				id: type_select.id,
				type: 'checkbox'
			});
			type_select.input.checked = false;
			type_select.input.injectTop(type_select);
			
			// For each input in sub-list
			type_select.inputs = $ES('input', type_select.types);
			for (var j = 0; j < type_select.inputs.length; j++) {
				type_select.inputs[j].type_select = type_select
				
				// Tick parent checkbox depending on if one of it's "child" checkboxes is ticked
				if (type_select.inputs[j].checked) {
					type_select.input.checked = true;
					type_select.addClass('checked');
				}
				
				// Change event that closes the section if all other checkboxes are unticked
				type_select.inputs[j].addEvent('click', function () {
					var checked = false;
					for (var i = 0; i < this.type_select.inputs.length; i++) {
						if (this.type_select.inputs[i].checked) checked = true;
					}
					if (!checked) {
						this.type_select.slide.slideOut();
						this.type_select.input.checked = false;
						this.type_select.removeClass('checked');
					}
				});
			}
			if (!type_select.input.checked) type_select.slide.hide();
			
			type_select.input.addEvent('click', function (e) {
				var type_select = $(this.parentNode);
				type_select.slide.toggle();
				var inputs = $ES('input', type_select.types);
				for (var i = 0; i < inputs.length; i++) {
					if (type_select.slide.open) {
						inputs[i].checked = false;
						inputs[i].parentNode.removeClass('checked');
					} else {
						inputs[i].checked = true;
						inputs[i].parentNode.addClass('checked');
					}
				}
			});
		}
	});

	new SmoothScroll();
	if ($('advertisements')) {
		var stylesheet = document.styleSheets[0];
		if (stylesheet.addRule) {
			stylesheet.addRule('#gspc', 'margin-left: 0;');
			var gspcCssRule = stylesheet.rules[stylesheet.rules.length-1];
		} else {
			stylesheet = new Element('style').injectInside($E('head'));
			stylesheet = document.styleSheets[document.styleSheets.length-1];
			stylesheet.media.appendMedium('screen');
			stylesheet.media.appendMedium('projection');
			stylesheet.insertRule('#gspc{ margin-left: 0; }', stylesheet.cssRules.length);
			var gspcCssRule = stylesheet.cssRules[stylesheet.cssRules.length-1];
		}
		
		var resizeForAdverts = function() {
			if (document.body.scrollWidth > 775) {
				var d = Math.min(205, document.body.scrollWidth - 775)
				gspcCssRule.style.marginLeft = (-d) + 'px';
				$('advertisements').setStyle('left', (document.body.scrollWidth - d) / 2);
			} else {
				gspcCssRule.style.marginLeft = '0px';
				$('advertisements').setStyle('left', '50%');
			}
		}
		window.addEvent('resize', resizeForAdverts);
		resizeForAdverts();
	}
	
	if ($('property_photos')) {
	
		function showThumbnail(event) {
			event = new Event(event);
			$('main_photo').src = this.href;
			event.stop();
		}
	
		$$('#property_photos ul a').each(function(element) {
			element.addEvent('click', showThumbnail.bindAsEventListener(element));
		});
	
	}
	
	if ($('search_results_map')) {
		new MapResults('search_results_map');
	}
	
	$ES('a.recaptcha_mailhide').addEvent('click', function(event) {
		event = new Event(event);
		
		var width = 500;
		var height = 300;
		var left = (screen.width - width) / 2;
		var top = (screen.height - height) / 2; 
		window.open(event.target.href, '_blank', 'toolbar=0,scrollbars=0,location=0,statusbar=0,menubar=0,resizable=0,width='+width+',height='+height+',left='+left+',top='+top);
		
		event.preventDefault();
	});
	
	if ($('valuation_more_info')) {
	
		var radios = $ES('input', 'field_more_information');
		var fx = new Fx.Slide('valuation_more_info');
		
		$($('valuation_more_info').parentNode).setStyle('clear', 'both');
		
		function showHideMoreInformation(fx) {
			if (this.checked) {
				fx.slideIn();
			} else {
				fx.slideOut();
			}
		}
		radios.each(function(radio) {
			$(radio).addEvent('click', showHideMoreInformation.bind(radios[0], fx)); 
		});
		if (!radios[0].checked) {
			fx.hide();
		}
		showHideMoreInformation.bind(radios[0])(fx);
	}
	
	// Find all forms with class "auto_submit" and hide their submit buttons
	// and make them auto-submit when any of their selects are changed. 
	$$('form.auto_submit').each(function(form) {
		$ES('select', form).each(function(select) {
			$(select).addEvent('change', function() {
				this.submit();
			}.bind(form));
		});
		$ES('input[type=submit]', form).each(function(element) {
			$(element).setStyle('display', 'none');
		});
	});
	
	if ($('quick_jump')) {
		var input = $E('input[type=text]', 'quick_jump');
		$('quick_jump').addEvent('submit', function(event) {
			var input = $E('input[type=text]', 'quick_jump');
			input.setStyle('background', '#fff url(' + MEDIA_URL + 'img/checking_reference.gif) no-repeat right');
			var reference = input.value;
			if (reference == '' || reference == 'Property ref. number') {
				alert('Please enter a GSPC reference number');
				new Event(event).stop();
				return;
			}
			var ajax = new Ajax('/property/exists/', {method: 'get', data: $('quick_jump').toQueryString()});
			ajax.addEvent('onSuccess', function(response) {
				input.setStyle('background', '#fff'); 
				var exists = Json.evaluate(response, true);
				if (exists) {
					location.href = $('quick_jump').action + '?' + $('quick_jump').toQueryString();
				} else {
					alert('No live property could be found with reference number "' + reference + '".\n\nPlease check and try again.');
				}
			});
			ajax.request();
			new Event(event).stop();
		});
	}
	
	
	$ES('#search_results div.result').each(function(result) {
		var input = $E('li.shortlist input[type=image]', result);
		if (input) {
			input.addEvent('mouseover', function() {
				if (!result.hasClass('shortlisted') && !result.hasClass('updating')) {
					this.src = MEDIA_URL + 'img/add_to_my_shortlist_hover.png';
				}
			}.bind(input));
			input.addEvent('mouseout', function() {
				if (!result.hasClass('shortlisted') && !result.hasClass('updating')) {
					this.src = MEDIA_URL + 'img/add_to_my_shortlist.png';
				}
			}.bind(input));
		}
	});
	
	updateShortlisted();
	
	initStylisedLabels();
	
	window.fireEvent('postdomready');

});


window.addEvent('load', function() {

	// Automatically replace all <div class="googlemap"> elements with real
	// Google Maps. The div must contain a link to Google Maps that contains
	// the latitude/longitude as part of its query string.
	$$('div.googlemap').each(function(element) {
		var href = $E('a', element).href;
		var matches = href.match(/q=([^&]*),([^&]*)/);
		if (matches) {
			var image = $E('img', element);
			var w = image.offsetWidth;
			var h = image.offsetHeight;
			var anchor = image.alt.split(',');
			var x = parseInt(anchor[0]);
			var y = parseInt(anchor[1]);
			var printImage = anchor[2];
			
			element.innerHTML = '<br /><br />Loading map...<br /><img src="' + MEDIA_URL + 'img/loading_map_2.gif" alt="" />'
			element.setStyle('text-align', 'center');
			
			var args = [matches[1], matches[2], image.src, printImage, w, h, x, y];
			
			Mercurytide.GoogleMaps.whenLoaded(function(lat, lng, image, printImage, imageWidth, imageHeight, anchorX, anchorY) {
				var map = new GMap2(this);
				var point = new GLatLng(lat, lng);
        		map.setCenter(point, 15);
        		map.addControl(new GSmallMapControl());
        		map.addControl(new GMapTypeControl());
        		Mercurytide.GoogleMaps.enableScrollWheelZoom(map);
        		map.enableContinuousZoom();
        		
				var icon = new GIcon();
				icon.image = image;
				icon.iconSize = new GSize(imageWidth, imageHeight);
				icon.iconAnchor = new GPoint(anchorX, anchorY);
				icon.printImage = printImage;
				icon.mozPrintImage = printImage;
				
				GEvent.addListener(map, 'zoomend', restrictZoom.bind(map));
				GEvent.addListener(map, 'move', restrictPan.bind(map));
				
        		map.addOverlay(new GMarker(point, {icon: icon, clickable: false}));
        	}.bind(element, args));
		}
	});

});


var Tack = new Class({
	initialize: function(point, xRadius, yRadius) {
		this.point = point;
		this.xRadius = xRadius || 0.005;
		this.yRadius = yRadius || 0.005;	
		this.marker = new TackOverlay(this.point, 0, 0);
		this.updateHalo();
	},
	updateHalo: function() {
		this.point = this.marker.getLatLng();
		var lat = this.point.lat();
		var lng = this.point.lng();
		var points = [];
		for (var i = 0; i < 360; i += 10) {
			points.push(new GLatLng(
				lat + (this.yRadius * Math.sin(i * Math.PI / 180)),
				lng + (this.xRadius * Math.cos(i * Math.PI / 180))
				));
		}
		points.push(points[0]);
		
		this.halo = new GPolygon(points, '#8D8C8C', 2, 1, '#ffffff', 0.75);
		
	},
	toJson: function() {
		return [
			this.point.lat().toFixed(5),
			this.point.lng().toFixed(5),
			this.yRadius.toFixed(5),
			this.xRadius.toFixed(5)
			];
	}
});
Tack.fromJson = function(json) {
	// Allow the old form for backward compatibility
	if(json.latitude != null)
		json = [json.latitude, json.longitude, json.latitudeRadius, json.longitudeRadius];
	return new Tack(new GLatLng(parseFloat(json[0]), parseFloat(json[1])), parseFloat(json[3]), parseFloat(json[2]));
}

Tack.MAX_HALO_RADIUS = 200; // pixels


var MoveTackAndHaloFx = Fx.Base.extend({
	initialize: function(tandh, options) {
		this.tandh = tandh;
		this.setOptions(options);
	},
	start: function(endingPoint) {
		this.startingPoint = this.tandh.getPosition();
		this.endingPoint = endingPoint;
		return this.parent(0, 1);
	},
	increase: function() {
		var x = this.startingPoint.x + (this.endingPoint.x - this.startingPoint.x) * this.now;
		var y = this.startingPoint.y + (this.endingPoint.y - this.startingPoint.y) * this.now;
		this.tandh.setPosition(x, y);
	}
});


var createAlphaImage = function(src) {
	if (window.ie6) {
		var element = new Element('div', {'styles': {
			'width': 100,
			'height': 100
		}});
		element.setSrc = function(src) {
			element.setStyle('filter', 'progid:DXImageTransform.Microsoft.AlphaImageLoader(src=' + src + ')');
		}
	} else {
		var element = new Element('img');
		element.setSrc = function(src) {
			element.src = src;
		}
	}
	element.setSrc(src);
	return element;
}

var createTackAndHalo = function(insertInto, pixelWidth, pixelHeight) {

	pixelWidth = pixelWidth || 0;
	pixelHeight = pixelHeight || 0;

	var approxPixelRadius = (pixelWidth + pixelHeight) / 4;
	if (approxPixelRadius > Tack.MAX_HALO_RADIUS) {
		pixelWidth = pixelHeight = null;
	}

	var container = new Element('div', {'styles': {'position': 'relative'}}).injectInside(insertInto);
	
	if (pixelWidth == 0 || pixelHeight == 0) {
		var halo = new Element('span').injectInside(container);
	} else if (pixelWidth && pixelHeight) {
		var halo = createAlphaImage('/property/map/halo/?w=' + pixelWidth + '&h=' + pixelHeight).injectInside(container);
	} else {
		var halo = createAlphaImage(MEDIA_URL + 'img/halo_haze.png').injectInside(container);
		pixelWidth = pixelHeight = 400;
	}
	var tack = createAlphaImage(MEDIA_URL + 'img/tack.png');
	tack.setStyles({
		'position': 'absolute',
		'left': pixelWidth / 2 - 14,
		'top': pixelHeight / 2 - 44
	});
	tack.injectInside(container);
	
	var preventDefault = function(event) { new Event(event).preventDefault(); };
	
	container.ondragstart = preventDefault;
	halo.ondragstart = preventDefault;
	tack.ondragstart = preventDefault;
	
	return {
		'container': container,
		'halo': halo,
		'tack': tack,
		'hotspot': tack,
		'getPosition': function() {
			var pos = tack.getPosition();
			return {x: pos.x + 14, y: pos.y + 44};		
		},
		'setPosition': function(x, y) {
			container.setStyle('left', x - pixelWidth / 2);
			container.setStyle('top', y - pixelWidth / 2);
		},
		'glowOn': function() {
			tack.setSrc(MEDIA_URL + 'img/tack_glow.png');
		},
		'glowOff': function() {
			tack.setSrc(MEDIA_URL + 'img/tack.png');
		},
		'effect': function(options) {
			return new MoveTackAndHaloFx(this, options);
		}
	};
}

// The TackOverlay class is created upon the first call to the constructor. 
var TackOverlay = function(latlng, pixelWidth, pixelHeight) {

	TackOverlay = function(latlng, pixelWidth, pixelHeight) {
		this.latlng = latlng;
		this.pixelWidth = pixelWidth;
		this.pixelHeight = pixelHeight;
	}
	TackOverlay.prototype = new GOverlay();
	TackOverlay.prototype.initialize = function(map) {
	
		var tandc = createTackAndHalo(document.body, this.pixelWidth, this.pixelHeight)
		this.element = tandc.container;
		this.element.setStyle('position', 'absolute');
		this.hotspot = tandc.hotspot;
		this.glowOn = tandc.glowOn;
		this.glowOff = tandc.glowOff;
	
		map.getPane(G_MAP_MARKER_PANE).appendChild(this.element);
		this.map = map;
	};
	TackOverlay.prototype.setPoint = function(latlng) {
		this.latlng = latlng;
	};
	TackOverlay.prototype.getLatLng = function(latlng) {
		return this.latlng;
	};
	TackOverlay.prototype.redraw = function(force) {
		var p = this.map.fromLatLngToDivPixel(this.latlng);
		var left = p.x - this.pixelWidth / 2;
		var top = p.y - this.pixelHeight / 2;
		
		this.element.style.left = left + "px";
		this.element.style.top = top + "px";
		this.element.style.zIndex = 99999999;
	};
	TackOverlay.prototype.remove = function() {
		this.element.remove();
	}
	TackOverlay.prototype.hide = function() {
		this.element.setStyle('display', 'none');
	}
	TackOverlay.prototype.show = function() {
		this.element.setStyle('display', 'block');
	}

	return new TackOverlay(latlng, pixelWidth, pixelHeight);

}


// The TooltipOverlay class is created upon the first call to the constructor. 
var TooltipOverlay = function(latlng, options) {

	TooltipOverlay = function(latlng, options) {
		this.latlng = latlng;
		this.text = options.text || '';
		this.offsetX = options.offsetX || 0;
		this.offsetY = options.offsetY || 0;
	}
	TooltipOverlay.prototype = new GOverlay();
	TooltipOverlay.prototype.initialize = function(map) {
		this.div = new Element('div', {
			'styles': {
				'background': '#ffc',
				'border': '1px solid #cc9',
				'font-size': '11px',
				'padding': '5px',
				'position': 'absolute',
				'white-space': 'nowrap'
				}
			});
		this.div.innerHTML = this.text;
		map.getPane(G_MAP_MARKER_PANE).appendChild(this.div);
		this.map = map;
	};
	TooltipOverlay.prototype.setPoint = function(latlng) {
		this.latlng = latlng;
	};
	TooltipOverlay.prototype.redraw = function(force) {
		var p = this.map.fromLatLngToDivPixel(this.latlng);
		var left = p.x + this.offsetX - this.div.offsetWidth / 2;
		var top = p.y + this.offsetY;
		
		// Ensure that the tooltip is shown within the map bounds
		var mapBounds = this.map.getBounds();
		var ne = this.map.fromLatLngToDivPixel(mapBounds.getNorthEast());
		var sw = this.map.fromLatLngToDivPixel(mapBounds.getSouthWest());
		if (left < sw.x) {
			left = sw.x;
		} else if (left + this.div.offsetWidth > ne.x) {
			left = ne.x - this.div.offsetWidth;
		}
		if (top + this.div.offsetHeight > sw.y) {
			top = p.y - this.offsetY - this.div.offsetHeight - 35;
		}
	
		this.div.style.left = left + "px";
		this.div.style.top = top + "px";
		this.div.style.zIndex = 99999999;
	};
	TooltipOverlay.prototype.remove = function() {
		this.div.parentNode.removeChild(this.div);
		this.div = null;
	}

	return new TooltipOverlay(latlng, options);

}


var MapSearch = new Class({
	mapLoaded: false,
	initialize: function(parent) {
	
		var div = new Element('div');
		div.injectInside(parent);
		
		var inputs = new Element('div');
		inputs.className = 'inputs';
		inputs.injectInside(div);
		
		this.element = new Element('div');
		this.element.id = 'mapsearch';
		this.element.injectInside(inputs);

		this.element.innerHTML = '<br /><br />Loading map...<br /><img src="' + MEDIA_URL + 'img/loading_map.gif" alt="" />'
		this.element.setStyles({
			'height': '400px',
			'text-align': 'center'
			});
		
		this.maxTacks = 10;
		this.tacks = [];	

	},
	loadMap: function() {
		if (this.mapLoaded) return;
		this.mapLoaded = true;
		Mercurytide.GoogleMaps.whenLoaded(this.initMap.bind(this));
	},
	initMap: function() {
		this.element.innerHTML = '';
		this.element.setStyles({
			'height': 'auto',
			'position': 'relative',
			'text-align': ''
			});
	
		var w = this.element.offsetWidth - 117;
	
		this.help = new Element('div', {'class': 'help'});
		this.help.adopt(new Element('div', {'class': 'double-click'}).adopt(new Element('div').setHTML('<strong>Zoom</strong> Double click')));
		this.help.adopt(new Element('div', {'class': 'drag'}).adopt(new Element('div').setHTML('<strong>Move</strong> Click & hold')));
		this.help.adopt(new Element('div', {'class': 'drag-tack'}).adopt(new Element('div').setHTML('<strong>Mark the area</strong> drag tacks onto the map')));
		this.help.injectInside(this.element);
	
		this.tackContainer = new Element('div', {'class': 'tack-container'});
		this.tackContainer.injectInside(this.element)
				
		this.trash = new Element('div', {'class': 'trash'})
		new Element('div').setHTML('Drag tacks<br /><strong>into the bin</strong><br />to remove').injectInside(this.trash);
		
		this.trashIcon = createAlphaImage(MEDIA_URL + 'img/where_map_trash_icon.png');
		this.trashIcon.addClass('trash-icon');
		this.trashIcon.injectInside(this.trash);
		
		this.trash.glowing = function() {
			return this.trash.hasClass('trash-hover');
		}.bind(this);
		
		this.trash.glowOn = function() {
			this.trashIcon.setSrc(MEDIA_URL + 'img/where_map_trash_icon_glow.png');
			this.trash.addClass('trash-hover');
		}.bind(this);
		
		this.trash.glowOff = function() {
			this.trashIcon.setSrc(MEDIA_URL + 'img/where_map_trash_icon.png');
			this.trash.removeClass('trash-hover');
		}.bind(this);
		
		this.trashIcon.addEvent('dblclick', function(event) {
			new Event(event).preventDefault();
			if (this.tacks.length > 1) {
				this.trash.glowOn();
				if (confirm('Do you really want to remove all tacks?')) {
					while (this.tacks.length > 0) {
						this.deleteTack(this.tacks[0]);
					}
				}
				this.trash.glowOff();
			}
		}.bind(this));
		

		
		[110, 85, 55].each(function(size) {
			var tandh = createTackAndHalo(this.tackContainer, size, size);
			tandh.container.addClass('tack');
			tandh.container.setStyle('margin-left', (110 - size) / 2);
		
			var successCallback = function() {
				tandh.container.setStyle('visibility', 'visible');
				tandh.container.setStyle('opacity', 0);
				var myEffects = new Fx.Styles(tandh.container, {
					duration: 200
				});
				myEffects.start({
				    'opacity': 1
				});
			}.bind(this);
			var failureCallback = function() {
				tandh.container.setStyle('visibility', 'visible');
			}.bind(this);
			
			tandh.container.addEvent('mouseover', function(event) {
				tandh.glowOn();
			});
			tandh.container.addEvent('mouseout', function(event) {
				tandh.glowOff();
			});
			
			tandh.container.addEvent('mousedown', function(event) {
				tandh.container.setStyle('visibility', 'hidden');
				this.tackMousedown(event, tandh.getPosition, size, size, successCallback, failureCallback, successCallback);
			}.bind(this));
		}.bind(this));
		
		this.tooMany = new Element('div', {'class': 'too-many', 'styles': {'display': 'none'}}).injectInside(this.tackContainer);
		this.tooMany.setHTML('<p><strong>You have added the maximum of ' + this.maxTacks + ' tacks.</strong></p><p>To add another tack, firstly remove an existing one by dragging it to the bin below.</p>');
		
		this.trash.injectInside(this.tackContainer);
		
		this.mapContainer = new Element('div', {'class': 'map'});
		this.mapContainer.setStyles({
			'border': '1px solid #a7a7a7',
			'height': '400px',
			'width': w
			});
		this.element.appendChild(this.mapContainer);
	
		this.map = new GMap2(this.mapContainer);
		var p = new GLatLng(MAP_CENTRE_LATITUDE, MAP_CENTRE_LONGITUDE);
        this.map.setCenter(p, 8);
        
        this.map.addControl(new GLargeMapControl());
		Mercurytide.GoogleMaps.enableScrollWheelZoom(this.map);
		this.map.enableContinuousZoom();

		GEvent.addListener(this.map, 'zoomend', restrictZoom.bind(this.map));
		GEvent.addListener(this.map, 'move', restrictPan.bind(this.map));

		var existingMapPointsData = $('id_map_points').value;
		if (existingMapPointsData) {
			this.unserialize(existingMapPointsData);
		}
		
		this.update();
       
	},
	
	
	tackMousedown: function(event, getPosition, pixelWidth, pixelHeight, successCallback, revertCallback, removeCallback) {
		event = new Event(event);
	
		// Stop the map itself from being dragged while a tack is being dragged
		this.map.disableDragging();
	
		var tandh = createTackAndHalo(document.body, pixelWidth, pixelHeight);
		tandh.container.setStyle('position', 'absolute');
		tandh.glowOn();
		
		var pos = getPosition();
		var dX = event.page.x - pos.x;
		var dY = event.page.y - pos.y;
	
		var mousemove = function(event) {
			if (!event.page) {
				event = new Event(event);
			}
			tandh.setPosition(event.page.x - dX, event.page.y - dY);
			tandh.container.setStyle('display', '');
			
			var trashTopLeft = this.trashIcon.getPosition();
			var trashSize = this.trashIcon.getSize().size;
			var trashCenter = {x: trashTopLeft.x + trashSize.x / 2, y: trashTopLeft.y + trashSize.y / 2};
			
			var trashDistance = Math.sqrt(
				Math.pow(trashCenter.x - (event.page.x - dX), 2) +
				Math.pow(trashCenter.y - (event.page.y - dY), 2)
			);
			
			if (trashDistance < 60) {
				this.trash.glowOn();
			} else {
				this.trash.glowOff();
			}  
			
		}.bind(this);
		
		mousemove(event);
		
		var mouseup = function(event) {
			event = new Event(event);
			document.removeEvent('mousemove', mousemove);
			this.map.enableDragging();
			
			var containerPos = this.mapContainer.getPosition();
		
			// The -1 is to account for border width
			var point = new GPoint(event.page.x - dX - containerPos.x + 1, event.page.y - dY - containerPos.y - 1);
			var ll = this.map.fromContainerPixelToLatLng(point);
			
			
			if (this.trash.glowing()) {
				this.trash.glowOff();
				tandh.container.remove();
				if (removeCallback) {
					removeCallback();
				}
				
			} else if (this.map.getBounds().contains(ll)) {
				// The Earth's curvature means that pixels don't map to lat/lng
				// linearly. We therefore need to calculate two radii, for the x
				// and y axes when drawing the circle using lat/lng.
	        	var projection = this.map.getCurrentMapType().getProjection();
	        	var zoom = this.map.getZoom();
	        	var pixelPoint = projection.fromLatLngToPixel(ll, zoom);
	        	var northwestPixel = new GPoint(pixelPoint.x - pixelWidth / 2, pixelPoint.y - pixelHeight / 2);
	        	var northwestLatLng = projection.fromPixelToLatLng(northwestPixel, zoom);
	        	var xRadius = Math.abs(northwestLatLng.lng() - ll.lng());
	        	var yRadius = Math.abs(northwestLatLng.lat() - ll.lat());
				
				var tack = new Tack(ll, xRadius, yRadius);
       			this.addTack(tack);
				
				tandh.container.remove();
				
				if (successCallback) {
					successCallback();
				}
			} else {
			
				tandh.effect({
					duration: 200,
					onComplete: function() {
						if (revertCallback) {
							revertCallback();
						}
						tandh.container.remove();
					}.bind(this)
				}).start(pos);
			}
			
			document.removeEvent('mouseup', mouseup);
		}.bind(this);
		
		document.addEvent('mouseup', mouseup);
		document.addEvent('mousemove', mousemove);
		
		event.preventDefault();
		event.stop();
	},
	
	serialize: function() {
		var hiddenValue = [];
		for (var i = 0; i < this.tacks.length; i++) {
			hiddenValue.push(this.tacks[i].toJson());
		}
		return Json.toString(hiddenValue);
	},
	
	unserialize: function(serialized) {
		var points = Json.evaluate(serialized, true);
		if (points.length > 0) {
			for (var i = 0; i < points.length; i++) {
	        	var tack = Tack.fromJson(points[i]);
	        	this.addTack(tack);
			}
			// Ensure existing tacks are visible
			var bounds = null;
			for (var i = 0; i < this.tacks.length; i++) {
				if (bounds == null) {
					bounds = this.tacks[i].halo.getBounds();
				} else {
					var tackBounds = this.tacks[i].halo.getBounds();
					bounds.extend(tackBounds.getNorthEast());
					bounds.extend(tackBounds.getSouthWest());
				}
			}
			var zoom = this.map.getBoundsZoomLevel(bounds);
			if (zoom >= MAX_ZOOM) {
				this.map.setCenter(bounds.getCenter(), zoom);
			} else {
				this.map.setCenter(this.tacks[0].point, MAX_ZOOM);
			}
		}
	},
	
	update: function() {
		$('id_map_points').value = this.serialize();
		if (this.tacks.length < this.maxTacks) {
			this.tackContainer.getElements('.tack').each(function(el) {
				el.setStyle('display', 'block');
			});
			this.tooMany.setStyle('display', 'none');
		} else {
			this.tackContainer.getElements('.tack').each(function(el) {
				el.setStyle('display', 'none');
			});
			this.tooMany.setStyle('display', 'block');
		}
	},
	
	addTack: function(tack, noUpdate) {
		this.map.addOverlay(tack.marker);
		this.map.addOverlay(tack.halo);
		this.tacks.push(tack);
		if (!noUpdate) this.update();
		
     	var mapTackMousedown = function(event) {
			event = new Event(event);
			
        	var projection = this.map.getCurrentMapType().getProjection();
        	var zoom = this.map.getZoom();
			
			var northeastLatLng = new GLatLng(tack.point.lat() + tack.yRadius, tack.point.lng() + tack.xRadius); 
			var southwestLatLng = new GLatLng(tack.point.lat() - tack.yRadius, tack.point.lng() - tack.xRadius);
			var northeastPixel = projection.fromLatLngToPixel(northeastLatLng, zoom);
			var southwestPixel = projection.fromLatLngToPixel(southwestLatLng, zoom);
			
			var pixelWidth = Math.abs(northeastPixel.x - southwestPixel.x);
			var pixelHeight = Math.abs(northeastPixel.y - southwestPixel.y);

			var pos = tack.marker.element.getPosition();
			getPosition = function() {
				return pos;
			}

			this.deleteTack(tack, true);

			successCallback = Class.empty;
			revertCallback = function() {
				this.addTack(tack);
			}.bind(this);
			removeCallback = function() {
				this.deleteTack(tack);
			}.bind(this); 

			this.tackMousedown(event, getPosition, pixelWidth, pixelHeight, successCallback, revertCallback, removeCallback);
			
			event.preventDefault();
		}
		
		tack.marker.hotspot.addEvent('mousedown', mapTackMousedown.bind(this));
		
		tack.marker.hotspot.addEvent('mouseover', function(event) {
			tack.marker.glowOn();
		});
		tack.marker.hotspot.addEvent('mouseout', function(event) {
			tack.marker.glowOff();
		});
		
	},
	
	deleteTack: function(tack, noUpdate) {
		this.map.removeOverlay(tack.marker);
		this.map.removeOverlay(tack.halo);
		this.tacks.remove(tack);
		if (!noUpdate) this.update();
	}
	
});


function simpleInitialize(data) {
	if ($chk(data)) {
		for (key in data) {
			if ($defined(this[key]) && $type(this[key]) != 'function') {
				this[key] = data[key];
			}
		}
	}
}

var Property = new Class({
	title: '',
	postcode: '',
	price: 0,
	offerType: 'Offers over',
	description: '',
	reference: '',
	latitude: 0,
	longitude: 0,
	isNew: false,
	url: '#',
	source: '',
	image: '#',

	initialize: simpleInitialize
});

var Cluster = new Class({
	title: '',
	latitude: 0,
	longitude: 0,
	minLatitude: 0,
	minLongitude: 0,
	maxLatitude: 0,
	maxLongitude: 0,

	initialize: simpleInitialize
});


var MapResults = new Class({
	initialize: function(element) {
		this.element = $(element);
		
		this.markers = [];
		
		this.element.innerHTML = 'Loading map...<br /><img src="' + MEDIA_URL + 'img/loading_map_2.gif" alt="" />'
		this.element.setStyles({
			height: 235,
			paddingTop: 200,
			position: 'relative',
			textAlign: 'center'
			});
				
		Mercurytide.GoogleMaps.whenLoaded(this.initMap.bind(this));
		
	},
	initMap: function() {
	
		this.element.setStyles({
			clear: 'both',
			height: 435,
			paddingTop: 0,
			textAlign: 'left'
			});
	
		this.initIcons();
		
		this.map = new GMap2(this.element);
		
		this.updatingPane = new Element('div', {
			styles: {
				background: '#fff',
				display: 'block',
				fontWeight: 'bold',
				left: 0,
				height: 235,
				opacity: 0.8,
				paddingTop: 200,
				position: 'absolute',
				textAlign: 'center',
				top: 0,
				width: '100%',
				zIndex: 99999
				}
			});
		this.updatingPane.innerHTML = 'Updating map...<br /><img src="' + MEDIA_URL + 'img/loading_map_2.gif" alt="" />';
		this.updatingPane.injectInside(this.element)
		
		this.positionBeforePropertyInfo = null;
		this.skipRefreshListener = null;
        this.refreshPointsListener = GEvent.addListener(this.map, 'moveend', this.refreshPoints.bind(this));

		// For some reason, Google Maps now thinks a non-existent InfoWindow
		// is visible, causing problems later on. Forcibly hide it now	...
		this.map.getInfoWindow().hide();

        this.map.addControl(new GLargeMapControl());
		Mercurytide.GoogleMaps.enableScrollWheelZoom(this.map);
		this.map.enableContinuousZoom();
		this.setExtents(function() {
			this.map.savePosition(); // Ensure the reset button works
			
	        var savedPosition = this.getSavedPosition();
	        if (savedPosition) {
	        	this.map.setCenter(new GLatLng(savedPosition.lat, savedPosition.lng), savedPosition.zoom);
	        }
	        
	        this.infoWindowOpen = false;
	        this.showingPropertyInfo = false;
	        
	        GEvent.addListener(this.map, 'zoomend', this.map.clearOverlays);
	        GEvent.addListener(this.map, 'zoomend', restrictZoom.bind(this.map));
	        GEvent.addListener(this.map, 'move', restrictPan.bind(this.map));
	        
	        GEvent.addListener(this.map, 'moveend', this.savePosition.bind(this));
	        
			GEvent.addListener(this.map, 'click', function() {
				if (this.showingPropertyInfo) {
					this.showingPropertyInfo = false;
				} else {
					if (this.infoWindowOpen) {
						if(this.positionBeforePropertyInfo) {
							// Pan back to the original point but don't trigger another refresh
							GEvent.removeListener(this.refreshPointsListener);
							this.refreshPointsListener = null;
							this.skipRefreshListener = GEvent.addListener(this.map, 'moveend', this.skipRefresh.bind(this));
							
							this.map.panTo(this.positionBeforePropertyInfo);
							this.positionBeforePropertyInfo = null;
						} else {
							this.refreshPoints();
						}
					}
					this.infoWindowOpen = false;
			  	}
			}.bind(this));
		}.bind(this));
        
	},
	initIcons: function() {
		this.NEW_PROPERTY_ICON = new GIcon();
		this.NEW_PROPERTY_ICON.image = MEDIA_URL + 'img/marker_new_property.png';
		this.NEW_PROPERTY_ICON.iconSize = new GSize(35, 35);
		this.NEW_PROPERTY_ICON.iconAnchor = new GPoint(10, 32);
	
		this.PROPERTY_ICON = new GIcon();
		this.PROPERTY_ICON.image = MEDIA_URL + 'img/marker_property.png';
		this.PROPERTY_ICON.iconSize = new GSize(35, 35);
		this.PROPERTY_ICON.iconAnchor = new GPoint(10, 32);
		
		this.PROPERTIES_ICON = new GIcon();
		this.PROPERTIES_ICON.image = MEDIA_URL + 'img/marker_properties.png';
		this.PROPERTIES_ICON.iconSize = new GSize(35, 35);
		this.PROPERTIES_ICON.iconAnchor = new GPoint(10, 32);

		this.MULTIPLE_PROPERTIES_ICON = new GIcon();
		this.MULTIPLE_PROPERTIES_ICON.image = MEDIA_URL + 'img/marker_multiple_properties.png';
		this.MULTIPLE_PROPERTIES_ICON.iconSize = new GSize(35, 35);
		this.MULTIPLE_PROPERTIES_ICON.iconAnchor = new GPoint(13, 13);
	},
	getSavedPositions: function() {
		var savedPositions = Cookie.get(MAP_COOKIE_KEY);
		if (savedPositions) {
			savedPositions = Json.evaluate(savedPositions, true);
		} else {
			savedPositions = {};
		}
		return savedPositions;
	},
	getSavedPosition: function() {
		return $pick(this.getSavedPositions()[location.search], null);
	},
	savePosition: function() {
		var savedPositions = this.getSavedPositions();
		var mapCenter = this.map.getCenter();
		savedPositions[location.search] = {
			lat: mapCenter.lat(),
			lng: mapCenter.lng(),
			zoom: this.map.getZoom()
			};
		// TODO -- ensure savedPositions can't get too big 
		Cookie.set(MAP_COOKIE_KEY, Json.toString(savedPositions), {
			path: '/'
			});
	},
	setExtents: function(callback) {
		var ajax = new Ajax('./extents/', {method: 'get', data: location.search.substring(1)});
		ajax.addEvent('onSuccess', function() {
			var extents = ajax.response.xml.getElementsByTagName('extents')[0]
			
			var total = parseInt(extents.getAttribute('total')); 
			if (total == 0) {
				this.map.setCenter(new GLatLng(MAP_CENTRE_LATITUDE, MAP_CENTRE_LONGITUDE), 10);
			} else {
				var top = parseFloat(extents.getAttribute('top'));
				var right = parseFloat(extents.getAttribute('right'));
				var bottom = parseFloat(extents.getAttribute('bottom'));
				var left = parseFloat(extents.getAttribute('left'));
		
				var ne = new GLatLng(top, right);
				var sw = new GLatLng(bottom, left);
				var mid = new GLatLng(bottom + (top - bottom) / 2, left + (right - left) / 2);
				
				var bounds = new GLatLngBounds(sw, ne);
				var zoom = this.map.getBoundsZoomLevel(bounds);
				
				this.map.setCenter(mid, zoom);
			}
			callback();
		}.bind(this));
		ajax.request();
	},
	
	refreshPoints: function() {

		// Don't refresh if there's an InfoWindow open
		if (!this.map.getInfoWindow().isHidden()) return;

        var bounds = this.map.getBounds();
        
        var ne = bounds.getNorthEast();
        var sw = bounds.getSouthWest();
        var w = sw.lng() - ne.lng();
        var h = sw.lat() - ne.lat();
        
        var ajaxData = location.search.substring(1) + '&' + Object.toQueryString({
			'top': ne.lat(),
			'right': ne.lng(),
			'bottom': sw.lat(),
			'left': sw.lng(),
			'_': new Date().getTime()
			}); 
        
        var ajax = new Ajax('./data/', {method: 'get', data: ajaxData});
		ajax.addEvent('onSuccess', function(ajax) {
			this.map.clearOverlays();
			this.propertiesByPostcode = {};
			
			var visibleCount = 0;
			
			var clusters = ajax.response.xml.getElementsByTagName('cluster');
			for (var i = 0; i < clusters.length; i++) {
				var count = parseInt(clusters[i].getAttribute('count'))
	        	this.addCluster(new Cluster({
	        		title: '<strong>' + intcomma(count) + ' properties in this area.</strong><br />Click to zoom.',
	        		latitude: clusters[i].getAttribute('latitude'),
	        		longitude: clusters[i].getAttribute('longitude'),
	        		minLatitude: clusters[i].getAttribute('top'),
	        		minLongitude: clusters[i].getAttribute('left'),
	        		maxLatitude: clusters[i].getAttribute('bottom'),
	        		maxLongitude: clusters[i].getAttribute('right')
	        	}));
	        	visibleCount += count;
			}
			var properties = ajax.response.xml.getElementsByTagName('property');
			visibleCount += properties.length;
			for (var i = 0; i < properties.length; i++) {
				var price = properties[i].getAttribute('asking_price');
				this.addProperty(new Property({
	        		title: properties[i].getAttribute('street'),
	        		postcode: properties[i].getAttribute('postcode'),
	        		latitude: properties[i].getAttribute('latitude'), 
	        		longitude: properties[i].getAttribute('longitude'),
	        		description: properties[i].getAttribute('description'),
	        		image: properties[i].getAttribute('image'),
	        		price: (price == '' ? null : parseInt(price)),
	        		reference: properties[i].getAttribute('reference'),
	        		isNew: !!parseInt(properties[i].getAttribute('is_new')),
	        		url: properties[i].getAttribute('url'),
	        		source: properties[i].getAttribute('source'),
	        		offerType: properties[i].getAttribute('offer_type')
	        	}));
	        }
	        
			var total = parseInt(ajax.response.xml.getElementsByTagName('results')[0].getAttribute('total'));
			$E('#search_results_heading h2').setText(intcomma(total) + ' results (' + intcomma(visibleCount) + ' visible on map)');
	        this.updatingPane.setStyle('display', 'none');
		}.bind(this, ajax));
		this.updatingPane.setStyle('display', 'block');
		ajax.request()
	
	},
	skipRefresh: function() {
		GEvent.removeListener(this.skipRefreshListener);
		this.skipRefreshListener = null;
		this.refreshPointsListener = GEvent.addListener(this.map, 'moveend', this.refreshPoints.bind(this));
	},
	addProperty: function(property) {
		
		var postcodeProperties = this.propertiesByPostcode[property.postcode];
		if (postcodeProperties) {
			this.map.removeOverlay(postcodeProperties[postcodeProperties.length - 1].marker);
			postcodeProperties.push(property);
			var icon = this.PROPERTIES_ICON;
			var title = postcodeProperties.length + ' properties at this postcode';
		} else {
			this.propertiesByPostcode[property.postcode] = [property];
			var icon = property.isNew ? this.NEW_PROPERTY_ICON : this.PROPERTY_ICON;
			var title = property.title;
		}
		
		var marker = new GMarker(new GLatLng(property.latitude, property.longitude), {
			icon: icon
		});
		GEvent.addListener(marker, 'click', this.showPropertyInfo.bind(this, property.postcode));
		
		var tooltip = new TooltipOverlay(new GLatLng(property.latitude, property.longitude), {
			offsetY: 10,
			text: '<strong>' + title + '</strong><br />Click for details.'
		});
		GEvent.addListener(marker, 'mouseover', function(tooltip) {
			this.map.addOverlay(tooltip);
		}.bind(this, tooltip));
		GEvent.addListener(marker, 'mouseout', function( tooltip) {
			this.map.removeOverlay(tooltip);
		}.bind(this, tooltip));
		
		property.marker = marker;
		this.map.addOverlay(marker);
	},
	addCluster: function(cluster) {
		var marker = new GMarker(new GLatLng(cluster.latitude, cluster.longitude), {
			icon: this.MULTIPLE_PROPERTIES_ICON
		});
		var tooltip = new TooltipOverlay(new GLatLng(cluster.latitude, cluster.longitude), {
			offsetY: 20,
			text: cluster.title
		});
		var sw = new GLatLng(cluster.minLatitude, cluster.minLongitude); 
		var ne = new GLatLng(cluster.maxLatitude, cluster.maxLongitude);  
		var points = [
			sw,
			new GLatLng(cluster.minLatitude, cluster.maxLongitude),
			ne,
			new GLatLng(cluster.maxLatitude, cluster.minLongitude),
			];
		points.push(points[0]);
		var polygon = new GPolyline(points);
		var bounds = new GLatLngBounds(sw, ne);
		GEvent.addListener(marker, 'mouseover', function(polygon, tooltip) {
			this.map.addOverlay(tooltip);
			this.map.addOverlay(polygon);
		}.bind(this, [polygon, tooltip]));
		GEvent.addListener(marker, 'mouseout', function(polygon, tooltip) {
			this.map.removeOverlay(tooltip);
			this.map.removeOverlay(polygon);
		}.bind(this, [polygon, tooltip]));
		GEvent.addListener(marker, 'click', function(marker, bounds) {
			this.map.removeOverlay(tooltip);
			this.map.removeOverlay(polygon);
			var zoom = this.map.getBoundsZoomLevel(bounds);
			this.map.setCenter(marker.getLatLng(), zoom);
		}.bind(this, [marker, bounds])); 
		this.map.addOverlay(marker);
	},
	showPropertyInfo: function(postcode) {

		this.showingPropertyInfo = true;

		if(!this.positionBeforePropertyInfo) {
			this.positionBeforePropertyInfo = this.map.getCenter();
		}

		var tabs = [];

		for (var i = 0; i < this.propertiesByPostcode[postcode].length; i++) {
			var property = this.propertiesByPostcode[postcode][i];

			/*
			<div class="map_window">
				<div class="info">
					<div class="price_info">
						<div class="price">&pound;{{ user|random_integer:"100,2000"|intcomma }},000</div>
						<div class="offer_type">Fixed price</div>
					</div>
				</div>
				<div class="details">
					<h3><a href="#">property.title</a></h3>
					<p>property.description</p>
					<div class="reference">property.reference</div>
					<div class="view_details"><a href="#">View property details</a></div>
				</div>
			</div>
			*/
			
			var div = new Element('div', {'class': 'map_window'});
			div.setStyles({
				width: 310
				});
	
			var info = new Element('div', {'class': 'info'});
			info.injectInside(div);
			
			var image_a = new Element('a', {href: property.url});
			if (property.source != 'GSPC') image_a.target = '_blank';
			image_a.injectInside(info);
			
			if (property.image != '') {
				var image = new Element('img', {'src': property.image, 'width': 120, 'height': 90});
			} else {
				var image = new Element('img', {'src': MEDIA_URL + 'img/no_image_small.png', 'width': 120, 'height': 90});
			}
			image.injectInside(image_a);
			
			var priceInfo = new Element('div', {'class': 'price_info'});
			priceInfo.injectInside(info);
			
			var price = new Element('div', {'class': 'price'});
			price.injectInside(priceInfo);
			price.innerHTML = '&pound;' + intcomma(property.price);
			
			var offerType = new Element('div', {'class': 'offer_type'});
			offerType.injectInside(priceInfo);
			offerType.innerHTML = property.offerType;
			
			var details = new Element('div', {'class': 'details'});
			details.injectInside(div);
			
			var h3 = new Element('h3');
			h3.injectInside(details);
			
			var a = new Element('a', {href: property.url});
			if (property.source != 'GSPC') a.target = '_blank';
			a.injectInside(h3);
			a.innerHTML = property.title;
			
			var p = new Element('p');
			p.injectInside(details);
			p.innerHTML = property.description;
			
			var reference = new Element('div', {'class': 'reference'});
			reference.injectInside(details);
			reference.innerHTML = 'Ref: ' + property.reference;
			
			var viewDetails = new Element('div', {'class': 'view_details'});
			viewDetails.injectInside(details);
			
			a = new Element('a', {href: property.url});
			if (property.source != 'GSPC') a.target = '_blank';
			a.injectInside(viewDetails);
			a.innerHTML = 'View property details';
			
			tabs.push(new GInfoWindowTab('Property ' + (tabs.length + 1), div));
			
			var latLng = new GLatLng(property.latitude, property.longitude);
		
		}

		if (tabs.length == 1) {				
			this.map.openInfoWindow(latLng, div, {maxWidth: 300});
		} else {
			this.map.openInfoWindowTabs(latLng, tabs, {maxWidth: 300});
		}
		
		this.infoWindowOpen = true;
		
		GEvent.addListener(this.map, 'closeclick', function() {
			this.infoWindowOpen = false;
		}.bind(this));
		
	}
	
});

function removeFromSavedSearches(result, id) {
	if (!confirm('Are you sure you want to delete this saved search?')) return;
	
	var ajax = new Ajax('/saved-searches/'+id+'/delete/', {method: 'post', data: 'submit=Yes&ajax=1'});
	ajax.addEvent('onSuccess', function (response) {
		if (response != "OK") return;
		result.parentNode.removeChild(result);
		search_count = $ES('#saved_searches table tr').length - 1;
		$('saved_search_count').innerHTML = search_count;
		if (search_count < 1) {
			no_searches = new Element('td', {
				'colspan': '4'
			});
			no_searches.innerHTML = noSavedSearchesChunk;
			var row = $E('#saved_searches tr');
			while (row.cells.length > 0) row.deleteCell(0);
			row.appendChild(no_searches);
		}
	});
	ajax.request()
}

function renewEmailSubscription(renew) {

	var form = $(renew).form;	
	var img = new Element('img', {'src': MEDIA_URL + 'img/renewing_subscription.gif', 'style': 'float: right'});
	$(renew).parentNode.appendChild(img);
	$(renew).setStyle('display', 'none');

	var ajax = new Ajax(form.action, {method: 'post', data: 'renew_email=1&ajax=1'});
	ajax.addEvent('onSuccess', function (response) {
		img.remove();
		var days = parseInt(response);
		if (isNaN(days)) {
			alert('An error was encountered while renewing your email subscription. Please try again.');
			$(renew).setStyle('display', '');
			return;
		}
		$E('#field_email_days_left .field').innerHTML = days + ' days left';
		$(renew).remove();
	});
	ajax.request();
	return false;
}

function renewSMSSubscription(id) {
	var ajax = new Ajax('/saved-searches/'+id+'/', {method: 'post', data: 'renew_sms=1&ajax=1'});
	ajax.addEvent('onSuccess', function (response) {
		if (response == "OK") {
			$E('#field_sms_days_left .field').innerHTML = "7 days";
		}
	});
	ajax.request()
}

function removeFromShortList(result) {
	$('search_results').removeChild(result)
	
	new_result_count = $ES('#search_results div.result').length
	$('search_results_heading').getElementsByTagName('h2')[0].innerHTML = new_result_count + " Shortlisted";
	$('my_shortlist').getElementsByTagName('strong')[0].innerHTML = "(" + new_result_count + ")"
	
	if (new_result_count < 1) {
		no_results = new Element('p');
		no_results.innerHTML = "You have no shortlisted items.";
		result = new Element('div', {
			'class': 'result'
		});
		result.appendChild(no_results);
		$('search_results').appendChild(result);
	}
}

function markAsShortlisted(result) {
	var form = $E('form.shortlist_form', result);
	var input = $E('input.toggle_shortlist', form);
	result.addClass('shortlisted');
	if (result.hasClass('prestige')) {
		result.addClass('shortlisted_prestige');
	}
	input.src = MEDIA_URL + 'img/remove_from_shortlist.png';
	input.alt = 'Remove from shortlist';
	form.action = '/shortlist/remove/';
	if (typeof onShortlistPage == 'undefined' || !onShortlistPage) {
		if (form.parentNode.nodeName == 'LI') { 
			var span = new Element('span');
			span.innerHTML = '(<a href="/shortlist/">view shortlist</a>)';
			span.injectInside(form.parentNode);
		}
	}
}

function markAsNotShortlisted(result) {
	var form = $E('form.shortlist_form', result);
	if (result.hasClass('no_details_available')) {
		form.remove();
		return;
	} 
	var input = $E('input.toggle_shortlist', form);
	result.removeClass('shortlisted');
	if (result.hasClass('prestige')) {
		result.removeClass('shortlisted_prestige');
	}
	input.src = MEDIA_URL + 'img/add_to_my_shortlist.png';
	input.alt = 'Add to my shortlist';
	form.action = '/shortlist/add/';
	if (form.parentNode.nodeName == 'LI') {
		var span = $E('span', form.parentNode);
		if (span) span.remove();
	}
}


function toggleShortlist(form, result) {
	result = $(result);
	var input = $E('input.toggle_shortlist', form);
	var counter = $E('strong', 'my_shortlist');
	
	result.addClass('updating');
	$('my_shortlist').addClass('updating');
	var oldsrc = input.src;
	var oldalt = input.alt;
	input.src = MEDIA_URL + 'img/updating_shortlist.gif';
	input.alt = 'Updating shortlist...';
	input.disabled = true;
	
	
	new Ajax(form.action, {
		data: $(form).toQueryString() + '&ajax=1',
		onFailure: function(response) {
			result.removeClass('updating');
			$('my_shortlist').removeClass('updating');
			input.src = oldsrc;
			input.alt = oldalt;
			input.disabled = false;
			alert("An error occurred, and your shortlist could not be updated.  Please try again shortly.");
		},
		onSuccess: function(response) {
			var count = parseInt(response);
			
			if (count == -1) {
				alert('Unable to add the property to your shorrlist.\nPlease ensure you have cookies enabled.');
				result.removeClass('updating');
				$('my_shortlist').removeClass('updating');
				input.src = oldsrc;
				input.alt = oldalt;
				input.disabled = false;
				return;
			}
			
			setTimeout(function(result, input, form, counter) {
				result.removeClass('updating');
				$('my_shortlist').removeClass('updating');
				input.disabled = false;
				if (result.hasClass('shortlisted')) {
					markAsNotShortlisted(result);
				} else {
					markAsShortlisted(result);
				}
				counter.innerHTML = '(' + (count) + ')';
			}.bind(this, [result, input, form, counter]), 1000);
			
		}}).request();

}

function updateShortlisted() {
	if (!$('search_results') && !$('property_details')) return;

	var ajax = new Ajax('/shortlist/property_identifiers/');
	ajax.addEvent('onSuccess', function(ajax) {
		var shortlistedList = Json.evaluate(ajax, true);
		var shortlisted = {}
		for (var i = 0; i < shortlistedList.length; i++) {
			var source = shortlistedList[i][0].toLowerCase();
			var reference = shortlistedList[i][1];
			shortlisted[source + '_' + reference] = true;
		}
		$ES('#search_results div.result').each(function(result) {
			if (shortlisted[result.id.substring(9)]) {
				if (!result.hasClass('shortlisted')) markAsShortlisted(result);			
			} else {
				if (result.hasClass('shortlisted')) markAsNotShortlisted(result);
			}
		});
		
		var result = $('property_details');
		if (result) {
			var property = result.getFirst()
			if (shortlisted[property.id.substring(9)]) {
				if (!result.hasClass('shortlisted')) markAsShortlisted(result);
			} else {
				if (result.hasClass('shortlisted')) markAsNotShortlisted(result);
			}
		}
		
		var counter = $E('strong', 'my_shortlist');
		counter.setText('(' + shortlistedList.length + ')');
	});
	ajax.request();
}

