/* ############################################ UTILITY FUNCTIONS ################################ */

var nn6=document.getElementById&&!document.all;
var searchData = {};
var budgetCalcLimits = {};

// IE array prototype methods

Array.prototype.contains = function (element) {
	for (var i = 0; i < this.length; i++) {
		if (this[i] == element) {
			return true;
		}
	}
	return false;
}

String.prototype.capitalize = function(){
    return this.replace(/\b[a-z]/g, function (s, n) {
        return s.toUpperCase();
    });
}

/* alert replacement with jquery impromptu */
function acalert(msg,timeout) {
	if(timeout > 0)
		$.prompt(msg,{ timeout: timeout });
	else
		$.prompt(msg);
}

/* model menu controller 3/11/2009 */
function initModelMenu(id) {

	if(id!='mota' && id.length > 0) {
		if(("div#blk_motabilitymenu"))
			$("div#blk_motabilitymenu").remove();
	}
	
	if(id!='ca99' && id!='mota')
		return;
		
	$("div#sidebar-right h2 span").each(function (i) {
		if($(this).html()=='Models')
			$(this).html('MANUFACTURERS');
      	});
      	
	if(("div#blk_franchisemenu"))
		$("div#blk_franchisemenu").remove();

}

function saveSearchData() {
	createCookie("searchcookie",JSON.stringify(searchData));
}

function saveBudgetCalcLimits() {
	createCookie("budgetcookie",JSON.stringify(budgetCalcLimits));
}

function clearSearchData() {
	//eraseCookie("searchcookie");
	// clear all apart from geocoding
	var searchDataTmp = {};
	if(searchData.postcode) {
		searchDataTmp.postcode = searchData.postcode;
		searchDataTmp.latitude = searchData.latitude;
		searchDataTmp.longitude = searchData.longitude;	
		searchData = searchDataTmp;
	}else{
		searchData = {};
	}
	saveSearchData();
}

function clearBudgetData() {
	eraseCookie("budgetcookie");
	budgetCalcLimits = {};
}

function clearFieldVal(e,cleartext) {
	if($(e).val() == cleartext) $(e).val('');
}

function ClearOptions(id,length) {
	document.getElementById(id).options.length = length;
}

function AddOption(id,label,value ) {
	var sel = document.getElementById(id);
	var opt = document.createElement("option");
	opt.value = value;
	if(nn6) {
		opt.innerHTML = label;
		sel.add(opt, null);
	} else {
		opt.text = label;
		sel.add(opt);
	}	
}

function CreateOption(text,value) {
	var o = document.createElement("option");
	o.text = text;
	o.innerHTML = text;
	o.value = value;
	return o;
}

function createOption(sel,val,label,selected) {
	var opt = document.createElement("option");
	opt.value = val;
	opt.selected=selected;
	if(nn6) {
		opt.innerHTML = label;
		sel.add(opt, null);
	} else {
		opt.text = label;
		sel.add(opt);
	}
}

function createCookie(name,value,days) {
	if (days) {
		var date = new Date();
		date.setTime(date.getTime()+(days*24*60*60*1000));
		var expires = "; expires="+date.toGMTString();
	}
	else var expires = "";
	document.cookie = name+"="+value+expires+"; path=/";
}

function readCookie(name) {
	var nameEQ = name + "=";
	var ca = document.cookie.split(';');
	for(var i=0;i < ca.length;i++) {
		var c = ca[i];
		while (c.charAt(0)==' ') c = c.substring(1,c.length);
		if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length);
	}
	return null;
}

function eraseCookie(name) {
	createCookie(name,"",-1);
}

function popFieldVal(e,val) {
	if(e.value == '') e.value = val;
}

function showHide(id,str) {
	document.getElementById(id).style.display = str;
}

function DisableEnableForm(xForm,xHow){
  objElems = xForm.elements;
  for(i=0;i<objElems.length;i++){
    objElems[i].disabled = xHow;
  }
}

// AC script
function rollbg(chosen, objectID) {
	if(chosen == "roll") {
		document.getElementById(objectID).className="roll";
	}else{
		document.getElementById(objectID).className="over";
	}
}


/* ############################################ CAR SEARCH  ########################################## */

var increm = 0;
var incamt = 0;
var totcar = 0;
var incdir = 0;
var curtot = 0;
var btnint = null;
var smabtn = null;

var search_type = "all";

function updateSearchMask(xml) {
	try {
		var roo = xml.getElementsByTagName("response")[0];
		var req = roo.getAttribute("request");

		if (roo.getElementsByTagName("variants").length == 1) {
				var os = roo.getElementsByTagName("variants")[0].getElementsByTagName("option");
				var variantlist="";
				for (var i=0;i<os.length;i++) {
					if(document.getElementById("variant"))
						AddOption("variant",os[i].getAttribute("value"),os[i].getAttribute("value"));
					if(i>0) 
						variantlist += ":";
					variantlist += os[i].getAttribute("value");
				}
				searchData.variantlist = variantlist;
			if(mask_repop && searchData.variant && document.getElementById("eco")) 
				document.getElementById("variant").value = searchData.variant;
		}else{
			delete searchData.variantlist;
		}

		//update # of cars available
		increm = 20;
		totcar = parseInt(roo.getAttribute("results"));
		
		searchData.totcar = totcar;
		saveSearchData();

		var diff = totcar > curtot ? totcar - curtot : curtot - totcar;
		incamt = Math.floor(diff/increm);
		if (incamt == 0) {
			incamt = 1;
			increm = diff;
		}
		incdir = curtot < totcar
		btnint = window.setInterval("updateOdo()",20);

		var min_price = roo.getAttribute("min_price") ? parseInt(roo.getAttribute("min_price")) : 2988;
		var max_price = roo.getAttribute("max_price") ? parseInt(roo.getAttribute("max_price")) : 150000;
		setPriceRangeOptions(req, min_price, max_price);
		
		if(document.getElementById("search_distance").value.length > 0) {
			var min_distance = roo.getAttribute("min_distance") ? parseInt(roo.getAttribute("min_distance")) : 10;
			var max_distance = roo.getAttribute("max_distance") ? parseInt(roo.getAttribute("max_distance")) : 1000;
			setDistanceRangeOptions(req, min_distance, max_distance);
		}
		
		mask_repop = false;	
		
	}catch(E){
		alert(E);
	}
}

function updateOdo() {
	if (increm > 0) {
		curtot = incdir ? curtot += incamt : curtot -= incamt;
		setOdo(curtot);
		increm--;
	}else{
		setOdo(totcar);
		window.clearInterval(btnint);
		if (totcar > 0)
			smabtn.disabled = false;
		btnint = null;
	}
}

function setOdo(number) {
	number = number + "";
	var clen = number.length;
	if(clen < 5) {
		for(i=clen;i<5;i++) {
			number = "0" + number;
		}
	}
	$("#counter").html("<span class=\"odometer\">"+number.substring(0,1)+"</span><span class=\"odometer\">"+number.substring(1,2)+"</span><span class=\"odometer\">"+number.substring(2,3)+"</span><span class=\"odometer\">"+number.substring(3,4)+"</span><span class=\"odometer\">"+number.substring(4,5)+"</span>");
}

function setDistanceRangeOptions(req, min, max) {
	var curr_distance = $("#search_distance").val();
	ClearOptions("search_distance",1);
	var distanceOptions = new Array(10,20,50,100,1000);
	for(var i=0;i<distanceOptions.length;i++) {
		var range = distanceOptions[i];
		if(range > min || range > max) {
			if(range != 1000) {
				AddOption("search_distance","Within "+ range + " miles",range);
			}else{
				AddOption("search_distance","National",range);
			}
		}
	}
	if(curr_distance.length > 0)
		$("#search_distance").val(curr_distance);
}

function setPriceRangeOptions(req, min, max) {

	var curr_min = ($("#min_price").val() == '') ? 0 : $("#min_price").val()-0;
	var curr_max = ($("#max_price").val() == '') ? 0 : $("#max_price").val()-0;
	min = getMinPrice(min)-0;
	max = getMaxPrice(max)-0;	
	ClearOptions("min_price",1);
	ClearOptions("max_price",1);
	var min_lower = 1988;
	var min_upper = 59998;
	var max_lower = 2000;
	var max_upper = 100000;
	var step = 1000;
	for(var i=min_lower;i<min_upper+1;i=i+step) {
		if(i>=min && i<=max) {
			if(curr_max == 0 || (curr_max > 0 && i<(curr_max-12))) {
				AddOption("min_price","\u00A3"+i,i);
			}
		}
		if(i > 14988) step = 5000;
	}
	step = 1000;
	
	// 24/8/2009 - CR
	if(curr_min > max) curr_min = max; 
	
	for(var i=max_lower;i<max_upper+1;i=i+step) {
		if(i>=min && i>(curr_min+12) && i<=(max+step)) {
			AddOption("max_price","\u00A3"+i,i);
		}
		if(i > 14988) step = 5000;	
	}
	
	if (max > 60000) {
		AddOption("max_price","\u00A360000+","100000");
	}
	
	if(searchData.budgetCalc)
		addBudgetPriceOptions();
	
	curr_min = curr_min+"";
	curr_max = curr_max+"";
	
	if(curr_min.length>0 && curr_min!=0)
		$("#min_price").val(curr_min+"");

	if(curr_max.length>0 && curr_max!=0)
		$("#max_price").val(curr_max+"");
	
}

function getMinPrice(min) {
	if(min<1988)
		min = 0;
	else
		min = 1000*Math.round(min/1000);
		min = min-12; // to get end in 88
	return min;
}

function getMaxPrice(max) {
	if(max>600000)
		max = 100000;
	else
		max = 1000*Math.round(max/1000);
	return max;
}

function resetForm(form,advanced,lookup) {

	search_type="all"; // get all makes
	
	for (var i=0;i<form.elements.length;i++) {
		if(form.elements[i].id != "search_postcode" && form.elements[i].id != "search_latitude" && form.elements[i].id != "search_longitude") {
			if (form.elements[i].tagName == "SELECT")
				form.elements[i].selectedIndex = 0;
			else if (form.elements[i].type == "text")
				form.elements[i].value = "";
			else if (form.elements[i].type == "checkbox")
				form.elements[i].checked = false;
		}
	}
	
	/* clear search_branch */
	if($('#search_branch').length > 0)
		$('#search_branch').val('');
	
	/* clear dynamic selects */
	var ff = new Array("model","variant");
	
	for(i=0;i<ff.length;i++) {
		if(document.getElementById(ff[i])) 
			ClearOptions(ff[i],1);
	}
	
	/* clear searchData */
	
	clearSearchData();
	
	// repopulate makes - as search type is reset
	
	ClearOptions("make",1);
	SetMakes();
	
	if(lookup) GetData($("#search_type"),null,false);
	
}

var geo_int = null;
var outcode = null;
var geo_doc = null;

function geocode(o) {

	geo_int = null;
	geo_doc = null;
	outcode = o.value.replace(/ /g,'');
	var pcl = outcode.length;
	if(pcl> 1) {	
		if(pcl > 4)
			outcode = outcode.substring(0,pcl-3);
		
		DisableEnableForm(document.SearchMaskForm,true);
		$("#SearchMaskFormContainer").toggleClass("maskFade");
		$("#geocoding").toggleClass("displayNone");
		$("#geocodingFull").toggleClass("displayNone");
		
		 $.ajax({
		   type: "GET",
		   cache: false,
		   url: "/_/applications/geocode.jsp",
		   data: "outcode="+outcode+"&postcode="+o.value+"&dt="+new Date(),
		   success: function(data){
				geo_doc = data;
				geo_int  = window.setInterval("setLatLon()",1000);
		   }
		 });
		
	}else{
		acalert("Please supply a postcode of at least 2 characters in length");
		$("#search_postcode").val('Postcode');
	}
	
}

function setLatLon() {

	window.clearInterval(geo_int);
	var roo = geo_doc.getElementsByTagName("response")[0];
	
	geo_int = null;
	DisableEnableForm(document.SearchMaskForm,false);
	$("#SearchMaskFormContainer").toggleClass("maskFade");
	$("#geocoding").toggleClass("displayNone");
	$("#geocodingFull").toggleClass("displayNone");
	
	if(roo.getAttribute("success") == "true") {
	
		var lat = roo.getAttribute("lat");
		var lon = roo.getAttribute("lon");
		$("#search_latitude").val(lat);
		$("#search_longitude").val(lon);
		showHide("distance_container","block");
		//$("#search_distance").val("1000");
		$("#search_postcode").val($("#search_postcode").val().toUpperCase());
		searchData.postcode = $("#search_postcode").val();
		searchData.latitude = lat;
		searchData.longitude = lon;
		searchData.search_orderby = "distance";
		saveSearchData();
		GetData($("#search_distance"),null,false);		
		// store cookie
		createCookie("geocode",$("#search_postcode").val() + "|" + lat + "|" + lon);		
		
	}else{
		acalert("Your postcode <b>" + $("#search_postcode").val() + "</b> appears to be invalid, please try another postcode");
		$("#search_postcode").val('Postcode');
	}

}
	
function SetMakes() {
		// populate makes based on search_type
		search_type = $("#search_type").val();
		for(var make in carData) {
			if(search_type != "all") {
				var models = carData[make];
				for (var i=0;i<models.length;i++){
					if(models[i][search_type]) { 
						AddOption("make",make,make);
						i = models.length;
					}
				}
			}else{
				AddOption("make",make,make);
			}
		}
}

function SetModels() {
	var src = $("#make");
	var models = carData[src.val()];
	for (var i=0;i<models.length;i++){
		if(search_type != "all") {
			if(models[i][search_type]) { 
				AddOption("model",models[i].model,models[i].model);
			}
		}else{
			AddOption("model",models[i].model,models[i].model);			
		}
	}
}

function GetData(src,dest,lookup) {	
	
	/* clear branch ID - as hiiden and may have been set */
	
	if($("#search_branch").length > 0)
		$("#search_branch").val('')
	
	/* store in searchData object */
	if(src.value == null || src.value.length == 0) {
		delete searchData[src.name];
	}else if (src.name=="images" || src.name=="eco") {
		if(src.checked) {
			searchData[src.name] = "1";
		}else{
			delete searchData[src.name];
		}
	}else{
		searchData[src.name] = src.value;
	}
	
	searchData.lastSearchType = $("#masktype").val();
		
	saveSearchData();
	
	if(dest=="make"  && src.value.length > 0) {
		/* if make - clear model and variant */
		ClearOptions("make",1);
		ClearOptions("model",1);
		if($("#variant").length > 0) ClearOptions("variant",1);
		SetMakes();
	}
	
	if(dest=="model" && src.value.length > 0) {
		ClearOptions("model",1);
		if($("#variant").length > 0) ClearOptions("variant",1);
		SetModels();
	} else if (dest=="model" && src.value.length == 0) {
		ClearOptions("model",1);
	}
	
	if(dest=="variant" && src.value.length > 0) {
		if($("#variant").length > 0) ClearOptions("variant",1);
	}
	
	/* get querystring for request */
	var pa = getSearchQS(src);
	
	if(!CheckPriceRange()) return;

	if($("#min_engine_size").length > 0) {
		if(!CheckEngineSizeRange()) return;
	}
	
	smabtn.disabled = true;
	
	 $.ajax({
	   type: "GET",
	   cache: false,
	   async: false,
	   url: "/_/applications/searchXML.jsp",
	   data: pa,
	   success: function(data){
			updateSearchMask(data);
	   }
	 });
	
}

function getSearchQS(src) {
	
	var el = document.getElementById("SearchMaskForm").elements;
	var pa = "req="+escape(src.name);
	var cp = src.id == "make" || src.id == "model" || src.id == "variant";
	for (var i=0;i<el.length;i++) {
			var v = cp && (el[i].name == "min_price" || el[i].name == "max_price") ? "" : el[i].value; /* reset prices if data lookup */
			if (el[i].type == "checkbox") {
				v = el[i].checked ? "1" : "0";
			}
			if(v.length > 0 && el[i].name) {
				pa += (pa==""?"?":"&")+escape(el[i].name)+"="+escape(v);
			}
	}
	if(pa.length > 0) pa += "&";
	pa += "dt="+new Date();
	return pa;
}

function CheckPriceRange() {
	var min_price = ($("#min_price").val() == "") ? 0 : $("#min_price").val()-0;
	var max_price = ($("#max_price").val() == "") ? 0 : $("#max_price").val()-0;
	if((min_price > 0 && max_price > 0) && (max_price <= min_price)) {
		acalert("Maximum Price must be more than the Minimum Price");
		//$("#max_price").val('');
		//$("#min_price").val('');
		return false;
	}
	return true;
}

function CheckEngineSizeRange() {
	var min_engine_size = ($("#min_engine_size").val() == "") ? 0 : $("#min_engine_size").val()-0;
	var max_engine_size = ($("#max_engine_size").val() == "") ? 0 : $("#max_engine_size").val()-0;
	if((min_engine_size > 0 && max_engine_size > 0) && (max_engine_size <= min_engine_size)) {
		acalert("Maximum Engine Size must be bigger than the Minimum Engine Size");
		$("#max_engine_size").val('');
		return false;
	}
	return true;
}

var advancedParams = new Array('transmission','variant','body_type','mileage','min_engine_size','max_engine_size','colour','doors','fuel_type','mpg','roadtax','age');

function clearAdvancedParams() {
	for(var i=0;i<advancedParams.length;i++) {
		delete searchData[advancedParams[i]];
	}
	saveSearchData();
}

var mask_repop = false; /* used to set variant on repop after ajax */

function repopulateSearchMask() {

	mask_repop = true;
	var src = "search_type";
	if(readCookie("searchcookie") == null) 
		return false;
	searchData = JSON.parse(readCookie("searchcookie"));
	
	/* see which searchmask we are populating */
	
	var masktype = $("#masktype").val();

	/* if short remove any advanced paramaters set, if been advanced search, clear short mask */
	//if(masktype == 'short') 
	//	clearAdvancedParams();
		
	//if(searchData.lastSearchType) {
	//	if(masktype == 'short' && searchData.lastSearchType == 'advanced') {
	//		clearSearchData();
	//		return;
	//	}
	//}
	
	if(searchData.budgetCalc)
		addBudgetPriceOptions();
		
	if($("#budgetresultsouter").length > 0) {
		// see if budget results is on page if so populate if we have data
		// if no range then hide element
		
		if(readCookie("budgetcookie") != null) {
			budgetCalcLimits = JSON.parse(readCookie("budgetcookie"));
			$("#budget_from").html(budgetCalcLimits.min_price);
			$("#budget_to").html(budgetCalcLimits.max_price);
			$("#budgetresultsouter").toggleClass("displayNone");
			$("#budgetnewsearch").toggleClass("displayNone");
		}
	}
	// repopulate branch data
	repopulateBranchData();
	
	// see if search
	
	if($("#make").length == 0)
		return false;

	// set current counter total 
	if(searchData.totcar) {
		totcar = searchData.totcar-0;
		curtot = searchData.totcar-0;
		setOdo(searchData.totcar);
		inmcrem = 0;
	}
		
	// set search type
	if(searchData.search_type)
		$("#search_type").val(searchData.search_type);
		
	// set makes and select based on search type
	ClearOptions("make",1);
	SetMakes();
	if(searchData.make && searchData.make.length > 0) {
		// set models and select option - based on make
		src = "make";
		$("#make").val(searchData.make);
		SetModels();
		if(searchData.model && searchData.model.length > 0) {
			src = "model";
			$("#model").val(searchData.model);
		}
	}
	
	// if there is a postcode then set postcode, lat, lon and distance
	
	if(searchData.postcode) {
		$("#search_postcode").val(searchData.postcode);
		// show distance select
		showHide("distance_container","block");
		$("#search_latitude").val(searchData.latitude);
		$("#search_longitude").val(searchData.longitude);
		$("#search_distance").val(searchData.distance);
	}
	
	// set min and max price range
	if(searchData.min_price  && searchData.min_price.length > 0)
		$("#min_price").val(searchData.min_price+"");
		
	if(searchData.max_price && searchData.max_price.length > 0)
		$("#max_price").val(searchData.max_price+"");
	
	// set images
	if(searchData.images)
		$("#images").attr('checked', true);
		
	if(!document.getElementById("eco"))
		return;
	
	// advanced only
	
	if(searchData.eco)
		$("#eco").attr('checked', true);
	
	var advancedParams = new Array('transmission','variant','body_type','mileage','min_engine_size','max_engine_size','colour','doors','fuel_type','mpg','roadtax','age','keywords');
	for(var i=0;i<advancedParams.length;i++) {
		if(searchData[advancedParams[i]]) {
			$("#" + advancedParams[i]).val(searchData[advancedParams[i]]+"");
		}
	}
	
	// if van set body type to van
	if(searchData.search_type == 'van' && $('#body_type').val().length == 0)
		$('#body_type').val('Van');
	
	// set filters
	
	if(searchData.search_orderby && searchData.search_orderby.length>0) {
		$("#search_orderby").val(searchData.search_orderby);
		$("#orderby").val(searchData.search_orderby+"");
	}else{
		if(masktype == 'advanced' && searchData.postcode) {
			$("#search_orderby").val('distance');
			$("#orderby").val('distance');
		}
	}
	if(searchData.search_list_view && searchData.search_list_view.length>0) {
		$("#search_list_view").val(searchData.search_list_view);
		$("#list_view").val(searchData.search_list_view+"");
	}
	if(searchData.search_per_page && searchData.search_per_page.length>0) {
		$("#search_per_page").val(searchData.search_per_page);
		$("#per_page").val(searchData.search_per_page+"");
	}
	if(searchData.search_branch && searchData.search_branch.length>0) {
		$("#search_branch").val(searchData.search_branch);
		$("#branch").val(searchData.search_branch+"");
	}
	
	if(searchData.variantlist) {
		// populate variants and if value set it
		var variantArray = searchData.variantlist.split(":");
		for(var i=0;i<variantArray.length;i++) {
			AddOption("variant",variantArray[i],variantArray[i]);
		}	
		if(searchData.variant && searchData.variant.length > 0)
			$("#variant").val(searchData.variant);
	}

}

function setFilter(e) {

	if(e.name == "orderby" && e.value == "distance" && $("#search_postcode").val() == "Postcode") {
		acalert("You must provide your postcode before you can order by distance");
		return false;
	}
	$("#search_"+e.name).val(e.value);
	searchData["search_"+e.name] = e.value;
	// TOCHECK - if($('#branch').val().length > 0) $("#search_branch").val($('#branch').val());
	saveSearchData();
	submitSearch();
}

function clearSearchFilters() {
	delete searchData.search_orderby;
	delete searchData.search_list_view;
	delete searchData.search_per_page;
	delete searchData.search_branch;
	saveSearchData();
	submitSearch();
}

function setTop5(make,model) {
	resetForm(document.SearchMaskForm,false,false);
	$("#make").val(make);
	ClearOptions("model",1);
	SetModels();
	$("#model").val(model);
	clearSearchData();
	searchData.make = make;
	searchData.model = model;
	saveSearchData();
	var pa = getSearchQS(document.SearchMaskForm);
	
	 $.ajax({
	   type: "GET",
	   cache: false,
	   async: false,
	   url: "/_/applications/searchXML.jsp",
	   data: pa,
	   success: function(data){
			submitTop5(data);
	   }
	 });
	
}

function submitTop5(xml) {
	try {
		var roo = xml.getElementsByTagName("response")[0];
		var req = roo.getAttribute("request");
		totcar = parseInt(roo.getAttribute("results"));
		searchData.totcar = totcar;
		saveSearchData();
		submitSearch();
	}catch(E){
		alert(E);
	}
}

function submitSearch() {

	// build search url
	var formaction = "/used-cars/";
	var optionstring = "";
	if($("#make").val().length > 0) {
		var make = $("#make").val();
		make = make.replace( new RegExp( " ", "g" ), "-" );
		formaction += make;
	}
	if($("#model").val().length > 0) {
		var model = $("#model").val();
		model = model.replace( new RegExp( " ", "g" ), "-" );
		formaction += "/" + model;
	}
	if(formaction != "/used-cars/") formaction += "/";
	// get all options, if any are set add /options/
	var coreParams = new Array('search_type','min_price','max_price','search_postcode','search_distance','images','search_latitude','search_longitude');
	var advParams = new Array('variant','colour','body_type','doors','mileage','fuel_type','age','mpg','min_engine_size','max_engine_size','roadtax','transmission','keywords','eco','search_orderby','search_list_view','search_per_page','search_branch');
	
	var distance = $("#search_distance").val();
	
	for(var i=0;i<coreParams.length;i++) {
		if((distance.length == 0 && coreParams[i].indexOf("search_") == -1) || distance.length > 0 || coreParams[i] == "search_type" || $("#search_latitude").val().length > 0) {
			if(coreParams[i] == "images") {
				if($("#"+coreParams[i]).attr('checked'))
					optionstring += coreParams[i] + "/1/";
			}else{
				var fval = $("#"+coreParams[i]).val();
				if(coreParams[i] == "search_postcode" && fval == "Postcode") fval = "";
				if(coreParams[i] == "search_type" && fval == "all") fval = "";
				if(fval.length > 0)
					optionstring += $("#"+coreParams[i]).attr("name") + "/" + $("#"+coreParams[i]).val() + "/";
			}
		}
	}
	
	if($("#eco").length > 0) {
		for(var i=0;i<advParams.length;i++) {
			if(advParams[i] == "eco") {
				if($("#"+advParams[i]).attr('checked'))
					optionstring += advParams[i] + "/1/";
			}else{
				var fval = $("#"+advParams[i]).val();	
				if(advParams[i] == "keywords" && fval == "Keywords e.g. TDi") fval = "";
				if(fval.length > 0) {
					optionstring += $("#"+advParams[i]).attr("name") + "/" + $("#"+advParams[i]).val().replace( new RegExp( "/", "g" ), "-" ) + "/";
				}
			}
		}
	}
	
	if(optionstring.length > 0)
		formaction += "options/" + optionstring;
		
	// if search_branch is empty, remove searchData.search_branch and save cookie, otherwise we perpetuate branch search	
	if($('#search_branch').val() == '') {
		delete searchData.search_branch;
		saveSearchData();
	}
		
	formaction = formaction.toLowerCase();
	document.SearchMaskForm.action = escape(formaction);
	document.SearchMaskForm.submit();
	
}

function setMakeModel(make,model) {
	clearSearchData();
	searchData.make = make;
	searchData.model = model;
	saveSearchData();
}


/* ############################################ BUDGET CALCULATOR  ########################################## */

function addBudgetPriceOptions() {
	$("#min_price option").each(function(i){
			var thisprice = $(this).val()-0;
			var nextOption = $(this).next();
			var nextprice = (nextOption.length > 0) ? nextOption.val()-0 : 100000;	
			var minp = searchData.min_price-0;
			if((minp > thisprice) && (minp < nextprice)) {
				var o = CreateOption("\u00A3"+minp,minp+"");
				$(o).insertBefore(nextOption);
			}
	});
	$("#max_price option").each(function(i){
			var thisprice = $(this).val()-0;
			var nextOption = $(this).next();
			var nextprice = (nextOption.length > 0) ? nextOption.val()-0 : 100000;	
			var maxp = searchData.max_price-0;
			if((maxp > thisprice) && (maxp < nextprice)) {
				var o = CreateOption("\u00A3"+maxp,maxp+"");
				$(o).insertBefore(nextOption);
			}
	});
}

function checkBudgetAPR() {
	var aprDisplay = ($('#calcpayment').val().length > 0 && $('#calcdeposit').val().length > 0) ? "block" : "none";
	document.getElementById("budgetAPR").style.display = aprDisplay;
}

var budgetCalc = {
	"flatrate" : 6,
	"lower_percentage" : 10,
	"upper_percentage" : 5,
	"min_price" : 0,
	"max_price" : 0,
	"calculate" : function() {
		calcBudget();
	},
	"showmask" : function() {
		$("#budgetresultsouter").toggleClass("displayNone");
		$("#budgetnewsearch").toggleClass("displayNone");		
	},
	"weekly_lower_limit" : 15,
	"weekly_upper_limit" : 200,
	"monthly_lower_limit" : 50,
	"monthly_upper_limit" : 1000	
};

function calcBudget() {

	var msg ="";
	var budget = 0;
	var upper_limit = 0;
	var lower_limit = 0;
	$("#budgetcalc_errormsg").html(msg);
	
	if($("#calcdeposit").val() == "" || $("#calcpayment").val() ==  "") {
		msg = "Please supply a payment and a deposit";
		$("#budgetcalc_errormsg").html(msg);
		return;
	}

	var weekly = ($("#calcpaymenttype").val() == "weekly") ? true : false;
	var periodinyears = $("#calcyears").val()-0;
	var deposit = $("#calcdeposit").val()-0;
	var payment = $("#calcpayment").val()-0;
	
	if(weekly) {
		if(payment<budgetCalc.weekly_lower_limit || payment > budgetCalc.weekly_upper_limit) {
			msg = "Your weekly payment must be between &pound;" + budgetCalc.weekly_lower_limit + " and &pound;" + budgetCalc.weekly_upper_limit;
			$("#budgetcalc_errormsg").html(msg);
			return;
		}
	}else if(payment<budgetCalc.monthly_lower_limit || payment > budgetCalc.monthly_upper_limit) {
		msg = "Your monthly payment must be between &pound;" + budgetCalc.monthly_lower_limit + " and &pound;" + budgetCalc.monthly_upper_limit;
		$("#budgetcalc_errormsg").html(msg);
		return;	
	}
	
	if(weekly) {
		budget = deposit + ((payment * 52) * periodinyears) / (1 + (periodinyears * ( (budgetCalc.flatrate-0) / 100)));
	}else{
		budget = deposit + ((payment * 12) * periodinyears) / (1 + (periodinyears * ( (budgetCalc.flatrate-0) / 100)));
	}
	
	if(budget>0) {
	
		clearBudgetData();
	
		upper_limit = Math.round(budget + (budget * (budgetCalc.upper_percentage / 100)));
		lower_limit = Math.round(budget - (budget * (budgetCalc.lower_percentage / 100)));
		
		if(upper_limit > 9) {
			upper_limit = upper_limit + "";
			upper_limit = upper_limit.substring(0,upper_limit.length-2)+"95";
		}	
		if(lower_limit > 9)	{
			lower_limit = lower_limit + "";
			lower_limit = lower_limit.substring(0,lower_limit.length-2)+"88";
		}
		
		// store in session cookie
		clearSearchData();
		searchData.min_price = lower_limit;
		searchData.max_price = upper_limit;
		budgetCalcLimits.min_price = lower_limit;
		budgetCalcLimits.max_price = upper_limit;
		// we need to find the total number of cars !!!! and store
		saveSearchData();
		saveBudgetCalcLimits();
		
		 $.ajax({
		   type: "GET",
		   cache: false,
		   async: false,
		   url: "/_/applications/searchXML.jsp?req=calc&min_price=" + lower_limit + "&max_price=" + upper_limit,
		   success: function(data){
				searchFromBudget(data);
		   }
		 });
			
	}
	
}

function searchFromBudget(xml) {

	try {
	
		var roo = xml.getElementsByTagName("response")[0];
		var totcar = parseInt(roo.getAttribute("results"));
		searchData.budgetCalc = "true";
		searchData.totcar = totcar;
		saveSearchData();
		//window.location.href = "/search-results.html?newsearch=1&min_price=" + searchData.min_price + "&max_price=" + searchData.max_price;
		window.location.href = "/used-cars/options/min_price/" + searchData.min_price + "/max_price/" + searchData.max_price+"/";
	
	}catch(E){
		alert(E);
	}

}

/* ############################################ SHOWROOM ########################################## */

//var showroom_lastclicked = null;
var showroom_numbertoshow = 5; // EDIT
//var showroom_numbertoshow = 10;
var showroom_limit = 10;
var showroom = {
	"total_cars" : 0,
	"stock_numbers" : [],
	"image_refs" : [],
	"image_found" : []		
};

function openMainShowroom() {
	if(showroom.stock_numbers < 1) {
		alert("There are no cars in your showroom");
		return;
	}
	window.parent.location = "http://www.gates.co.uk/Ford/myshowroom.asp";
}

function storeShowroomStockNumbers() {
	var sn = showroom.stock_numbers+"";
	var myRegExp = new RegExp(",","g");
	var result = sn.replace(myRegExp, "|");
	createCookie("showroomsns",result);
}

function repopulateShowroom() {

	if(readCookie("showroomcookie") != null) 
		showroom = JSON.parse(readCookie("showroomcookie"));
		
	$("#showroom-loading").remove();
	createShowroom();
}

function addCarToShowroom(sn,picfnd) {

	//if(showroom.stock_numbers.contains(sn)) return false; // EDIT
	if(showroom.stock_numbers.contains(sn)) {
	alert("This car is already in your showroom",2000);
		return false;
	}
	if(showroom.total_cars >= showroom_limit) {
		alert("You already have the maximum of "+showroom_limit+" cars in your showroom");
		return false;
	}
	// get image href
	
	if(picfnd == "yes") {
		var img_src = "http://www.gates.co.uk/data/images/"+sn+"_1.jpg";
		//return true;
	}else if(picfnd == "no") {
		var img_src = "http://www.gates.co.uk/images/nocar-77778s.gif";
		//return true;
	}
	
	showroom.total_cars = showroom.total_cars + 1;
	showroom.stock_numbers.push(sn);
	showroom.image_refs.push(img_src); // was unshift EDIT
	// showroom.image_refs.push("data/images/"+sn+"_01.jpg");
	showroom.image_found.push(picfnd); // ADDITION : was an image found when vehicle added to cookie?
	//scroll(0,0);
	saveShowroom();
	createShowroom();
	//acalert("Car added to showroom",2000); // EDIT
	alert("Car added to showroom (picture: "+picfnd+")",2000);
}


function checkforcarinShowroom(sn) {

	// Checks cookie for existing vehicle (used in cardesc_NEW // EDIT
	if (showroom.stock_numbers.contains(sn)) {
	alert("This car is already in your showroom",2000);
			return false;
	}
	if(showroom.total_cars >= showroom_limit) {
		alert("You already have the maximum of "+showroom_limit+" cars in your showroom");
		return false;
	}
	
	
}


/*
function clearShowroom_ORIG() {
	$.prompt("Are you sure you want to clear your showroom?",{
	//prompt("Are you sure you want to clear your showroom?",{ // EDIT
		  callback: clearShowroomConf,
		  buttons: { Yes: 'yes', No: 'no' } 
	});
}
*/


function clearShowroom() {
	var answer = confirm("Are you sure you want to clear your showroom?")
	if (answer){
	showroom.total_cars = 0;
	showroom.stock_numbers = [];
	showroom.image_refs = [];
	showroom.image_found = [];
	saveShowroom();
	createShowroom();
	}
	else{
		//alert("It didn't work!")
	}
}




function clearShowroomConf(v) {
	if(v == 'no') return;
	showroom.total_cars = 0;
	showroom.stock_numbers = [];
	showroom.image_refs = [];
	showroom.image_found = [];
	saveShowroom();
	createShowroom();
}

function removeShowroomCar(arr,id) {
	var newArray = new Array();
	var j=0;
	for(var i=0;i<arr.length;i++) {
		if(arr[i] != id) {
			newArray[j] = arr[i];
			j++;
		}	
	}
	return newArray;
}

function getShowroomIndex(arr,id) {
	var j=0;
	for(var i=0;i<arr.length;i++) {
		if(arr[i] == id) {
			j=i;
		}	
	}
	return j;
}

function deleteCarFromShowroom(id) {
	showroom.total_cars = showroom.total_cars - 1;
	/* TODO - splice doesn't work in IE - prototype these methods */
	var showroomIndex = getShowroomIndex(showroom.stock_numbers,id);
	showroom.image_refs = removeShowroomCar(showroom.image_refs,showroom.image_refs[showroomIndex]);
	showroom.stock_numbers = removeShowroomCar(showroom.stock_numbers,id);
	//showroom.stock_numbers.splice(showroom.stock_numbers.indexOf(id), 1);
	//showroom.image_refs.splice(showroom.stock_numbers.indexOf(id), 1);
	saveShowroom();
	createShowroom();		
}

function loadShowroomCarousel() {
	$(".showroom_slide").jCarouselLite({
	//$("parent.shwroom.showroom_slide").jCarouselLite({									   
		btnNext: ".default .next",
		btnPrev: ".default .prev",
		visible: showroom_numbertoshow
	}); 		
}

function createShowroom() {

	$("#showroom-count").html("My Showroom ("+showroom.total_cars+")");

	var newcar_showroom = "<div class='showroom_slide'>";
	newcar_showroom += "<ul id='cars'>";
	for(var i=0;i<showroom.total_cars;i++) {
//		newcar_showroom += "<li class='showroom_thumb_li' id='car"+i+"'><a href='cardesc_NEW.asp?id="+showroom.stock_numbers[i]+"'><img border='0' class='showroom_thumb' id='srm_"+showroom.stock_numbers[i]+"' src='"+showroom.image_refs[i]+"'/></a></li>"; //EDIT
		newcar_showroom += "<li class='showroom_thumb_li' id='car"+i+"'><a href='../../../Ford/cardesc_NEW.asp?id="+showroom.stock_numbers[i]+"' target='_parent'><img border='0' class='showroom_thumb' id='srm_"+showroom.stock_numbers[i]+"' src='"+showroom.image_refs[i]+"' alt='"+showroom.stock_numbers[i]+"' title='"+showroom.stock_numbers[i]+"'/></a></li>"; //EDIT
	}
	if(showroom.total_cars < showroom_numbertoshow) {
		for(var i=showroom.total_cars;i<showroom_numbertoshow;i++) {
//		newcar_showroom += "<li class='showroom_thumb_li' id='emptycar_"+i+"'><img class='showroom_thumb_empty' src='/images/template/showroom/cars/empty.png'/></li>"; EDIT
//		newcar_showroom += "<li class='showroom_thumb_li' id='emptycar_"+i+"'><img class='showroom_thumb_empty' src='http://www.gates.co.uk/images/00ccff.gif'/></li>";
		newcar_showroom += "<li class='showroom_thumb_li' id='emptycar_"+i+"'><img class='showroom_thumb_empty' src='http://www.gates.co.uk/images/blank_showroom.gif'/></li>";
		}
	}
	newcar_showroom += "</ul>";
	newcar_showroom += "</div>";		
	$(".showroom_slide").remove();
	$("#showroom_slide_cell").append(newcar_showroom);
	loadShowroomCarousel();
}

function saveShowroom() {
	createCookie("showroomcookie",JSON.stringify(showroom));
	storeShowroomStockNumbers();
}

function deleteFromMainShowroom() {

	$(".showroom_checkbox").each(function (i,checkbox) {
		// if checkbox is checked, get its id, remove row - remove from carousel
		if(checkbox.checked) {
			var myRegExp = new RegExp("showroom_","g");
			var stockref = (checkbox.id).replace(myRegExp, "");
			$("#row_"+stockref).remove();
			deleteCarFromShowroom(stockref);
			// if no cars in showroom - show message and hide buttons
			if(showroom.total_cars == 0) {
				$("#showroom_main").toggleClass("displayNone");
				$("#emptied_showroom").toggleClass("displayNone");
			}
		}
	});

}

function compareShowroom(ctype) {
	var qs = "?srs=";
	var ct = 0;
	var limit = (ctype == 'compare') ? 2 : 1;
	$(".showroom_checkbox").each(function (i,checkbox) {
		if(checkbox.checked) {
			var myRegExp = new RegExp("showroom_","g");
			var stockref = (checkbox.id).replace(myRegExp, "");
			qs += stockref + ",";
			ct++;
		}
	});	
	if(ct >= limit) {
		qs = qs.substring(0,(qs.length-1));
		if(ctype == 'compare') {
			if(ct > 6) {
				acalert("You can only compare up to a maximum of 6 cars");
				return false;
			}
			window.location = "/showroom/compare-cars.html" + qs;
		}
		if(ctype == 'enquire')
			window.location = "/showroom/vehicle-enquiry.html" + qs;
	}else{
		if(ctype == 'compare')
			msg = "You must select a minimum of 2 cars to compare";
		if(ctype == 'enquire')
			msg = "You must select at least one car to enquire";
		acalert(msg);
	}
}

function compareSelectAll() {
	$(".showroom_checkbox").attr('checked', true);
}

function compareDelete() {
	var qs = "";
	var loc = "";
	$(".showroom_checkbox").each(function (i,checkbox) {
		if(!checkbox.checked) {
			var myRegExp = new RegExp("showroom_","g");
			var stockref = (checkbox.id).replace(myRegExp, "");
			qs += stockref + ",";
		}
	});
	if(qs.length == 0) {
		//loc =  "/showroom/"; //EDIT
		loc =  "#";
	}else{
		qs = qs.substring(0,(qs.length-1));
		loc = "/showroom/compare-cars.html?srs=" + qs;
	}
	window.location = loc;
}

/* ################################ FRANCHISE LOGOS ######################## */

var franACLogo = null;

function initFranchiseMenu() {

	$(".franchiseMenuLink").each(function () {
		$(this).mouseover(function(){
			$("#franchiseRolloverLogo").attr("src",this.rel);
			window.clearInterval(franACLogo);
		}).mouseout(function(){
			franACLogo = window.setInterval("setFranchiseLogoAC()",1000);
		});		
	});

}

function setFranchiseLogoAC() {
	$("#franchiseRolloverLogo").attr("src","/images/franchise/logos/arnoldclark.png");
	window.clearInterval(franACLogo);
}

/* ################################ CAREERS ######################## */

var hiRow = null;
function hilightCareerRow(r) 
{
	if (document.getElementById) 
	{
 		if (hiRow && hiRow.className != "careerRowActive") hiRow.className = "careerRow";
		hiRow = r;
		if (hiRow && hiRow.className != "careerRowActive") hiRow.className = "careerRowHover";
 	}
}

/* ################################ SITE WIDE ONLOAD FUNCTIONS ######################## */

$(document).ready(function(){

	smabtn = document.getElementById("redbutton");

	loadShowroomCarousel();
	repopulateSearchMask();
	repopulateShowroom();
	
	if($("#branch_search_location").length > 0) {
		$("#branch_search_location").autocomplete("/_/applications/getLocations.jsp", {
			width: 150
		});
	}
	
	jQuery(".numbersOnly").keyup(function () { 
	   this.value = this.value.replace(/[^0-9]/g,'');
	});
	
	$("#showhidebutton").click(function () {
      $("#vertical_slide").slideToggle("slow");
      var ntext = ($(this).html() == "Hide") ? "Show" : "Hide";
      $(this).html(ntext);
      $(this).toggleClass("advancedsearch_hidebutton");
    });
    
    // if no search results on page, and advanced mask open advanced searchmask
    // 12/7/09 - keep open
	//if($(".advanced_searchmask").length > 0 && $("#resultswrapper").length == 0)
	//if($(".advanced_searchmask").length > 0)
	//	$("#showhidebutton").click();
	
});

function accistSetHowHear() {
	if($("#company_code").val().length > 0) {
		$("#how_heard").addClass("displayNone");
		$("#how_heard_corporate").removeClass("displayNone");
		// add corporate option to select - and select it
		if($("#how_heard").find("option[value='Corporate member']").html() != "Corporate member"){
			AddOption("how_heard","Corporate member","Corporate member");
		}
		$("#how_heard").val("Corporate member");

	}else{
		$("#how_heard").removeClass("displayNone");
		$("#how_heard_corporate").addClass("displayNone");
		// remove corporate option from select
		 $("#how_heard option[value='Corporate member']").remove();  
	}
}

/* JSON */

/*
    http://www.JSON.org/json2.js
    2009-04-16

*/

if (!this.JSON) {
    JSON = {};
}
(function () {

    function f(n) {
        // Format integers to have at least two digits.
        return n < 10 ? '0' + n : n;
    }

    if (typeof Date.prototype.toJSON !== 'function') {

        Date.prototype.toJSON = function (key) {

            return this.getUTCFullYear()   + '-' +
                 f(this.getUTCMonth() + 1) + '-' +
                 f(this.getUTCDate())      + 'T' +
                 f(this.getUTCHours())     + ':' +
                 f(this.getUTCMinutes())   + ':' +
                 f(this.getUTCSeconds())   + 'Z';
        };

        String.prototype.toJSON =
        Number.prototype.toJSON =
        Boolean.prototype.toJSON = function (key) {
            return this.valueOf();
        };
    }

    var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
        escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
        gap,
        indent,
        meta = {    // table of character substitutions
            '\b': '\\b',
            '\t': '\\t',
            '\n': '\\n',
            '\f': '\\f',
            '\r': '\\r',
            '"' : '\\"',
            '\\': '\\\\'
        },
        rep;


    function quote(string) {

// If the string contains no control characters, no quote characters, and no
// backslash characters, then we can safely slap some quotes around it.
// Otherwise we must also replace the offending characters with safe escape
// sequences.

        escapable.lastIndex = 0;
        return escapable.test(string) ?
            '"' + string.replace(escapable, function (a) {
                var c = meta[a];
                return typeof c === 'string' ? c :
                    '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
            }) + '"' :
            '"' + string + '"';
    }


    function str(key, holder) {

// Produce a string from holder[key].

        var i,          // The loop counter.
            k,          // The member key.
            v,          // The member value.
            length,
            mind = gap,
            partial,
            value = holder[key];

// If the value has a toJSON method, call it to obtain a replacement value.

        if (value && typeof value === 'object' &&
                typeof value.toJSON === 'function') {
            value = value.toJSON(key);
        }

// If we were called with a replacer function, then call the replacer to
// obtain a replacement value.

        if (typeof rep === 'function') {
            value = rep.call(holder, key, value);
        }

// What happens next depends on the value's type.

        switch (typeof value) {
        case 'string':
            return quote(value);

        case 'number':

// JSON numbers must be finite. Encode non-finite numbers as null.

            return isFinite(value) ? String(value) : 'null';

        case 'boolean':
        case 'null':

// If the value is a boolean or null, convert it to a string. Note:
// typeof null does not produce 'null'. The case is included here in
// the remote chance that this gets fixed someday.

            return String(value);

// If the type is 'object', we might be dealing with an object or an array or
// null.

        case 'object':

// Due to a specification blunder in ECMAScript, typeof null is 'object',
// so watch out for that case.

            if (!value) {
                return 'null';
            }

// Make an array to hold the partial results of stringifying this object value.

            gap += indent;
            partial = [];

// Is the value an array?

            if (Object.prototype.toString.apply(value) === '[object Array]') {

// The value is an array. Stringify every element. Use null as a placeholder
// for non-JSON values.

                length = value.length;
                for (i = 0; i < length; i += 1) {
                    partial[i] = str(i, value) || 'null';
                }

// Join all of the elements together, separated with commas, and wrap them in
// brackets.

                v = partial.length === 0 ? '[]' :
                    gap ? '[\n' + gap +
                            partial.join(',\n' + gap) + '\n' +
                                mind + ']' :
                          '[' + partial.join(',') + ']';
                gap = mind;
                return v;
            }

// If the replacer is an array, use it to select the members to be stringified.

            if (rep && typeof rep === 'object') {
                length = rep.length;
                for (i = 0; i < length; i += 1) {
                    k = rep[i];
                    if (typeof k === 'string') {
                        v = str(k, value);
                        if (v) {
                            partial.push(quote(k) + (gap ? ': ' : ':') + v);
                        }
                    }
                }
            } else {

// Otherwise, iterate through all of the keys in the object.

                for (k in value) {
                    if (Object.hasOwnProperty.call(value, k)) {
                        v = str(k, value);
                        if (v) {
                            partial.push(quote(k) + (gap ? ': ' : ':') + v);
                        }
                    }
                }
            }

// Join all of the member texts together, separated with commas,
// and wrap them in braces.

            v = partial.length === 0 ? '{}' :
                gap ? '{\n' + gap + partial.join(',\n' + gap) + '\n' +
                        mind + '}' : '{' + partial.join(',') + '}';
            gap = mind;
            return v;
        }
    }

// If the JSON object does not yet have a stringify method, give it one.

    if (typeof JSON.stringify !== 'function') {
        JSON.stringify = function (value, replacer, space) {

// The stringify method takes a value and an optional replacer, and an optional
// space parameter, and returns a JSON text. The replacer can be a function
// that can replace values, or an array of strings that will select the keys.
// A default replacer method can be provided. Use of the space parameter can
// produce text that is more easily readable.

            var i;
            gap = '';
            indent = '';

// If the space parameter is a number, make an indent string containing that
// many spaces.

            if (typeof space === 'number') {
                for (i = 0; i < space; i += 1) {
                    indent += ' ';
                }

// If the space parameter is a string, it will be used as the indent string.

            } else if (typeof space === 'string') {
                indent = space;
            }

// If there is a replacer, it must be a function or an array.
// Otherwise, throw an error.

            rep = replacer;
            if (replacer && typeof replacer !== 'function' &&
                    (typeof replacer !== 'object' ||
                     typeof replacer.length !== 'number')) {
                throw new Error('JSON.stringify');
            }

// Make a fake root object containing our value under the key of ''.
// Return the result of stringifying the value.

            return str('', {'': value});
        };
    }


// If the JSON object does not yet have a parse method, give it one.

    if (typeof JSON.parse !== 'function') {
        JSON.parse = function (text, reviver) {

// The parse method takes a text and an optional reviver function, and returns
// a JavaScript value if the text is a valid JSON text.

            var j;

            function walk(holder, key) {

// The walk method is used to recursively walk the resulting structure so
// that modifications can be made.

                var k, v, value = holder[key];
                if (value && typeof value === 'object') {
                    for (k in value) {
                        if (Object.hasOwnProperty.call(value, k)) {
                            v = walk(value, k);
                            if (v !== undefined) {
                                value[k] = v;
                            } else {
                                delete value[k];
                            }
                        }
                    }
                }
                return reviver.call(holder, key, value);
            }


// Parsing happens in four stages. In the first stage, we replace certain
// Unicode characters with escape sequences. JavaScript handles many characters
// incorrectly, either silently deleting them, or treating them as line endings.

            cx.lastIndex = 0;
            if (cx.test(text)) {
                text = text.replace(cx, function (a) {
                    return '\\u' +
                        ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
                });
            }

// In the second stage, we run the text against regular expressions that look
// for non-JSON patterns. We are especially concerned with '()' and 'new'
// because they can cause invocation, and '=' because it can cause mutation.
// But just to be safe, we want to reject all unexpected forms.

// We split the second stage into 4 regexp operations in order to work around
// crippling inefficiencies in IE's and Safari's regexp engines. First we
// replace the JSON backslash pairs with '@' (a non-JSON character). Second, we
// replace all simple value tokens with ']' characters. Third, we delete all
// open brackets that follow a colon or comma or that begin the text. Finally,
// we look to see that the remaining characters are only whitespace or ']' or
// ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval.

            if (/^[\],:{}\s]*$/.
test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@').
replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']').
replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {

// In the third stage we use the eval function to compile the text into a
// JavaScript structure. The '{' operator is subject to a syntactic ambiguity
// in JavaScript: it can begin a block or an object literal. We wrap the text
// in parens to eliminate the ambiguity.

                j = eval('(' + text + ')');

// In the optional fourth stage, we recursively walk the new structure, passing
// each name/value pair to a reviver function for possible transformation.

                return typeof reviver === 'function' ?
                    walk({'': j}, '') : j;
            }

// If the text is not JSON parseable, then a SyntaxError is thrown.

            throw new SyntaxError('JSON.parse');
        };
    }
}());