/**
  * This script is copyrighted by Pieter Coucke (The Reference)
  */   
var suggestions = new Array();
var selectedIndex = 0;
var previouslyEntered = "";
var MAX_SUGGESTIONS_LENGTH = 20;
// This array is used as a buffer to hold the current index of the cities 
// and postal code array, so we don't need to start searching the array 
// from the beginning but can start from the previously found index.
var lastIndex = new Array(0,0);
var postalCodePfx;
var cPfx;
var uIdPfx;


function clearField(field)
{
	var fieldToClear = document.getElementById(field);
	fieldToClear.value = "";
}

/**
  * Check on country to enable/disable the functionality.
  */
function checkCountry(value,check) {
	if (value == check)
		init(0);
	else
		cleanUp();
}
function checkCountryX(value,check,x) {
	if (value == check)
		init(x);
	else
		cleanUp(x);
}

/**
  * Makes the layer with id 'layerId' visible according to 'isVisible'.
  */
function setVisible(element,isVisible) {
	if (element != null) {
		if (isVisible) {
			element.style.display = "block";
		} else {
			element.style.display = "none";
		}
	}
}

/**
  * Disables the element with id "id" if "isDisabled" is 
  * true, otherwise enables the element.
  */
function setDisabled(id, isDisabled) {
	document.getElementById(id).disabled=isDisabled;
}

function isUnsupportedBrowser() {
	return false;
}

function initPostalCodes(postalCodePrefix, cityPrefix, uniqueIdPrefix){
	postalCodePfx = postalCodePrefix;
	cPfx = cityPrefix;
	uIdPfx = uniqueIdPrefix;
	init(0);
}

function init(x) {
	/* If the browser is not supported, don't initialize the javascript callbacks. */
	if (isUnsupportedBrowser()) {
		return;
	}
	/*
	 * Initialize all postcode fields in the page.  They all need to be named like
	 * postalCodePrefix + sequence (starting with 1)
	 */
	var i = 1;
	var ok = true;
	if (x > 0) i = x;
	while (ok) {
		 var postalCodeId = postalCodePfx + i;
		var cityId = cPfx + i;
		var uniqueId = uIdPfx + i;

		var postalCodeObj = document.getElementById(postalCodeId);
		var cityObj = document.getElementById(cityId);
		var uniqueIdObj = document.getElementById(uniqueId);
	
		ok = (postalCodeObj != null) && (cityObj != null) && (uniqueIdObj != null);
		if (ok) {
			initPostalCodeDropdowns(postalCodeObj,cityObj,uniqueIdObj);
		}
		if (x > 0) ok = false;
		i++;
	}
	/*
	 * Bugfix for IE which leaks memory if inner functions (closures) are used that references variables.
	 * More info on http://msdn.microsoft.com/library/default.asp?url=/library/en-us/ietechcol/dnwebgen/ie_leak_patterns.asp
	 * The solution is to explicitly set all event handlers to null.
	 */
	window.onunload = function() {
		cleanUp();
	}
}

function cleanUp(x) {
	i = 1;
	var ok = true;
	if (x > 0) i = x;
	while (ok) {
		var postalCodeId = postalCodePfx + i;
		var cityId = cPfx + i;

		var postalCodeObj = document.getElementById(postalCodeId);
		var cityObj = document.getElementById(cityId);

		ok = (postalCodeObj != null) && (cityObj != null);
		if (ok) {
		    postalCodeObj.onfocus = null;
		    postalCodeObj.onkeydown = null;
		    postalCodeObj.onkeyup = null;
		    postalCodeObj.onblur = null;
		    
		    cityObj.onfocus = null;
		    cityObj.onkeydown = null;
		    cityObj.onkeyup = null;
		    cityObj.onblur = null;
		}
		if (x > 0) ok = false;
		i++;
	}
}

/**
  * Cache the arrays and select box variable, so these don't need to be recalculated
  * every time.
  */
function initPostalCodeDropdowns(postalCodeObj, cityObj, uniqueIdObj) {

	var postalCodeSuggestions = document.getElementById(postalCodeObj.id + "Suggestions");
	var citySuggestions = document.getElementById(cityObj.id + "Suggestions");

	initSuggestionBox(postalCodeObj,postalCodeSuggestions,citySuggestions,postalCodeObj,cityObj,citiesSortedByPostalCode,postalCodeSuggestions,citySuggestions,1,getValuePostalCode,uniqueIdObj,0);
	initSuggestionBox(cityObj,citySuggestions,postalCodeSuggestions,postalCodeObj,cityObj,citiesSortedByCity,postalCodeSuggestions,citySuggestions,2,getValueCity,uniqueIdObj,1);

	// If the unique id field is already filled in (eg. after a form submission error), then set the 
	// postalcode and city to this value.
	if (uniqueIdObj.value.length > 0) {
		var i = 0;
		var found = false;
		var value = new Number(uniqueIdObj.value);
		
		// Don't pre-fill the fields if there is already a value present
		// (which may be possible when a form can't be validated and is shown again)
		if (postalCodeObj.value.length == 0 && cityObj.value.length == 0) {
		
			while (i < citiesSortedByPostalCode.length && !found) {
				if (citiesSortedByPostalCode[i][0] == value) {
					found = true;
					postalCodeObj.value = citiesSortedByPostalCode[i][1];
					cityObj.value = citiesSortedByPostalCode[i][2];
				}
				i++;
			}
			
		}
	}

}

function initSuggestionBox(inputObj, inputSuggestions, otherSuggestions, postalCodeObj, cityObj, citiesArray, postalCodeSuggestions, citySuggestions, arrayIndex, getValueFunction, uniqueIdObj, lastIndexPosition) {
	// Set the functions here so we can avoid putting them in the html
	inputObj.onfocus = function() {
		showSuggestions(inputObj.value,citiesArray,inputSuggestions,arrayIndex,true,getValueFunction,postalCodeObj, cityObj, postalCodeSuggestions, citySuggestions, uniqueIdObj, lastIndexPosition);
	}
	
	inputObj.onkeydown = function(event) {
		setVisible(otherSuggestions,false);
		var isNavigationPress = handleEnter(inputSuggestions, postalCodeObj, cityObj, postalCodeSuggestions, citySuggestions, uniqueIdObj, event);
		if (isNavigationPress) {  // entered
			if (!event) {
				event = window.event;
  			}
  			event.returnValue = false;
			return false;
		}
	}
	
	inputObj.onkeyup = function(event) {
		var isNavigationPress = handleCursor(inputSuggestions, postalCodeObj, cityObj, postalCodeSuggestions, citySuggestions, uniqueIdObj, event);
		if (!isNavigationPress) {
			showSuggestions(inputObj.value,citiesArray,inputSuggestions,arrayIndex,true,getValueFunction,postalCodeObj, cityObj, postalCodeSuggestions, citySuggestions, uniqueIdObj, lastIndexPosition);
		}
	}
	
	inputObj.onblur = function() {
		
		// If the user decides to remove his postal code selection, then remove the hidden 
		// field too, since this will be submitted
		if (inputObj.value.length == 0) {
			uniqueIdObj.value = "";
		}

	    // Use a timeout here because this function is called before the onclick handler of 
        // a suggestion line.  Without a timeout, the line is already hidden before it is clicked.
		window.setTimeout('setVisible(document.getElementById("' + inputSuggestions.id + '"),false);',200);

		setSelectsVisible(true);
	}

}

/**
  * Remove all child nodes of the suggestionsObj node and the attached event listeners 
  * since these cause memory leaks in IE.
  */
function removeAllSuggestions(node) {
	if (node && node.hasChildNodes && node.removeChild) {
		while (node.hasChildNodes()) {
			var child = node.firstChild;
			child.onclick = null;
			child.onmouseover = null;
			child.onmouseout = null;
			node.removeChild(child);
		}
	}
}

/*
 * The 'force' variable can be used to display the suggestions even if there hasn't changed anything in the
 * content of the inputfield.  This can be useful when the user focuses on the inputfield.
 */
function showSuggestions(enteredValue,citiesArray,suggestionsObj,arrayIndex,force,valueFunction, postalCodeObj, cityObj, postalCodeSuggestions, citySuggestions, uniqueIdObj, lastIndexPosition) {

	if (enteredValue.length > 0) {
	
		setSelectsVisible(false);

		enteredValue = enteredValue.toLowerCase();	
		if (enteredValue != previouslyEntered || force) {

			// Instead of searching every time from 0, start from the index that was found by the last 
			// typed character.  If the user deletes a character, then start searching again from 
			// the start.
			var i = 0;
			previouslyEntered = enteredValue;
			removeAllSuggestions(suggestionsObj);
			suggestions = new Array();
			selectedIndex = 0;
	
			/* A variable to keep track of the last match that has been found.  If we come to
			 * the point where the we have found something (lastFound != -1), but it wasn't the previously
			 * checked postal code, we can stop checking any further postal code since these are sorted ascendingly.
			 */
			var lastFound = -1;
			while (i < citiesArray.length && (lastFound == -1 || lastFound == i-1) && suggestions.length < MAX_SUGGESTIONS_LENGTH) {
				var city = citiesArray[i];
				var currentValue = city[arrayIndex].toString().substring(0,enteredValue.length);
				if (currentValue.toLowerCase() == enteredValue) {
				
					var isFirstMatch = (lastFound == -1);
					lastFound = i;
					
					var lineDiv = document.createElement("div");
					lineDiv.appendChild(document.createTextNode(valueFunction(city)));
					var idString = "suggestion_" + suggestions.length;
					lineDiv.id = idString;
					lineDiv.className = isFirstMatch ? "suggestionSelected" : "suggestionNotSelected";
					suggestionsObj.appendChild(lineDiv);
									

					lineDiv.onclick = function () {
						var id = new Number(this.id.substring(11));
						highlightDiv(suggestionsObj,id);
						selectedIndex = id;
						fillWithSelectedOption(postalCodeObj, cityObj, postalCodeSuggestions, citySuggestions, uniqueIdObj);
					};
					
					lineDiv.onmouseover = function() {
						var id = new Number(this.id.substring(11));
						highlightDiv(suggestionsObj,id);
					};

					lineDiv.onmouseout = function() {
						var id = new Number(this.id.substring(11));
						changeSuggestion(suggestionsObj,id,"suggestionNotSelected");
					};
	
					suggestions.push(city);
					
				}
				i++;
			}
			
			var isVisible = (lastFound != -1 && suggestions.length > 0);
			setVisible(suggestionsObj,isVisible);
		}
		
	}
}

function selectCurrentDiv(lineDiv,postalCodeObj, cityObj, postalCodeSuggestions, citySuggestions, uniqueIdObj) {
	var divId = new Number(lineDiv.id.substring(11));
	highlightDiv(lineDiv.parentNode,divId);
	selectedIndex = divId;
	fillWithSelectedOption(postalCodeObj, cityObj, postalCodeSuggestions, citySuggestions, uniqueIdObj);
}

function highlightCurrentDiv(lineDivId) {
	var divId = new Number(lineDivId.substring(11));
	var lineDiv = document.getElementById(lineDivId);
	highlightDiv(lineDiv.parentNode,divId);
}

function unHighlightCurrentDiv(lineDivId) {
	var divId = new Number(lineDivId.substring(11));
	var lineDiv = document.getElementById(lineDivId);
	changeSuggestion(lineDiv.parentNode,divId,"suggestionNotSelected");
}

function getValuePostalCode(city) {
	var value = city[1];
	if (city[3] == 1) {
		value += " - ";
	}
	value += " " + city[2];
	return value;
}

function getValueCity(city) {
	return city[2] + " (" + city[1] + ")";
}

function highlightDiv(suggestionsObj, index) {
	changeSuggestion(suggestionsObj,selectedIndex,"suggestionNotSelected");
	changeSuggestion(suggestionsObj,index,"suggestionSelected");
}

function changeSuggestion(suggestionsObj, index, className) {
	var suggestion = suggestionsObj.childNodes[index];
	if (suggestion != null) {
		suggestion.className = className;
	}
}

function fillWithSelectedOption(postalCodeObj, cityObj, postalCodeSuggestions, citySuggestions, uniqueIdObj) {
	if (suggestions.length > 0 && selectedIndex >= 0) {
		var city = suggestions[selectedIndex];
		postalCodeObj.value = city[1];
		cityObj.value = city[2];
		uniqueIdObj.value = city[0];
	} else {
		uniqueIdObj.value = "";
	}

	window.setTimeout('setVisible(document.getElementById("'+postalCodeSuggestions.id+'"),false);',100);
	window.setTimeout('setVisible(document.getElementById("'+citySuggestions.id+'"),false);',100);
	setSelectsVisible(true);
	// IE Memory cleanup
	removeAllSuggestions(postalCodeSuggestions);
	removeAllSuggestions(citySuggestions);
}

/**
 * Handles a keypress event if it is up, down or enter.  If this is the case, 
 * return true, otherwise return false.
 */
function handleCursor(suggestionsObj, postalCodeObj, cityObj, postalCodeSuggestions, citySuggestions, uniqueIdObj, oEvent) {
	if (!oEvent) {
       oEvent = window.event;
  	}
	switch(oEvent.keyCode) {
		case 38: //up arrow
			if (selectedIndex > 0) {
				highlightDiv(suggestionsObj,selectedIndex-1);
				selectedIndex--;
			}
			return true;
		case 40: //down arrow
			if (selectedIndex < suggestions.length-1) {
				highlightDiv(suggestionsObj,selectedIndex+1);
				selectedIndex++;
			}
			return true;
	}
	return false;
};
function handleEnter(suggestionsObj, postalCodeObj, cityObj, postalCodeSuggestions, citySuggestions, uniqueIdObj, oEvent) {
	if (!oEvent) {
       oEvent = window.event;
  	}
	switch(oEvent.keyCode) {
		case 39: //right arrow = enter
			fillWithSelectedOption(postalCodeObj, cityObj, postalCodeSuggestions, citySuggestions, uniqueIdObj);
			return true;
		case 13: //enter
			fillWithSelectedOption(postalCodeObj, cityObj, postalCodeSuggestions, citySuggestions, uniqueIdObj);
			return true;
	}
	return false;
};


/**
  * Bugfix for IE which let selectboxes shine through a layer.
  * The solution here is to hide all selectboxes with the class "inputSelectHideable" 
  * until the layer is hidden again.
  * An alternative solution is to put an iframe around the select boxes, but this
  * doesn't work on < IE 5.5.  An improvement of this script would be to check for the 
  * specific IE version and hide the select boxes or put an iframe around them according 
  * to the version.
  */
function setSelectsVisible(visible){
	// only for IE
	if (document.all) {
		for(x=0;x<document.forms.length;x++){
			for(i=0;i<document.forms[x].length;i++){
				var tempobj=document.forms[x].elements[i];
				if (tempobj.type.substring(0,6) == "select" && tempobj.className == "inputSelectHideable"){
					tempobj.style.visibility = visible ? "visible" : "hidden";
				}
			}
		}
	}
}