/**
  * This script is copyrighted by Pieter Coucke (The Reference)
  */ 
//var citiesSortedByCity = new Array();
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);

/*function CityComparator(array1, array2) {
	if (array1[2] < array2[2]) {
		return -1;
	} else if (array1[2] > array2[2]) {
		return 1;
	} else {
		return 0;
	}
}*/

function isUnsupportedBrowser() {
	return false;
}

function initPostalCodes(postalCodePrefix, cityPrefix, uniqueIdPrefix) {

	/*
	 * 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;
	while (ok) {
		var postalCodeId = postalCodePrefix + i;
		var cityId = cityPrefix + i;
		var uniqueId = uniqueIdPrefix + 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) {
		    /*// Only sort once (when needed)
			if (citiesSortedByCity.length == 0) {
				for(j=0; j < citiesSortedByPostalCode.length; j++) {
					citiesSortedByCity.push(citiesSortedByPostalCode[j]);
				}
				citiesSortedByCity.sort(CityComparator);
			}*/
		
			initPostalCodeDropdowns(postalCodeObj,cityObj,uniqueIdObj);
		}
		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() {
	i = 1;
	var ok = true;
	while (ok) {
		var postalCodeId = postalCodePrefix + i;
		var cityId = cityPrefix + i;

		var postalCodeObj = document.getElementById(postalCodeId);
		var cityObj = document.getElementById(cityId);

		ok = (postalCodeObj != null) && (cityObj != null);
		if (ok) {
			//alert("Cleaning up " + postalCodeId + " and " + cityId);
		    postalCodeObj.onfocus = null;
		    postalCodeObj.onkeyup = null;
		    postalCodeObj.onblur = null;
		    
		    cityObj.onfocus = null;
		    cityObj.onkeyup = null;
		    cityObj.onblur = null;
		}
		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.onkeyup = function(event) {
		setVisible(otherSuggestions,false);
		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);
		//window.setTimeout('setVisible(document.getElementById("' + citySuggestions.id + '"),false);',200);

		setSelectsVisible(true);

		// The problem with the line below is that it is called when the user clicks on a line in 
		// the suggestions, so the line is hidden and the click-event there is lost
		//fillWithSelectedOption(postalCodeObj, cityObj, postalCodeSuggestions, citySuggestions, uniqueIdObj);
	}

}

/**
  * 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;
			/*if (enteredValue.length > previouslyEntered.length) {
				//alert(lastIndex[lastIndexPosition]);
				i = lastIndex[lastIndexPosition];
			}*/

			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++;
			}
			
			/*if (i > 0) {
				lastIndex[lastIndexPosition] = i-1;
			}
			
			if (lastIndex[lastIndexPosition] < 0) {
				lastIndex[lastIndexPosition] = 0;
			}*/
			
			var isVisible = (lastFound != -1 && suggestions.length > 0);
			//window.setTimeout('setVisible(document.getElementById("'+suggestionsObj.id+'"),'+isVisible+')',20);
			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) {
	//alert("Highlighting index " + 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);
	//setVisible(postalCodeSuggestions,false);
	//setVisible(citySuggestions,false);
	setSelectsVisible(true);
	// IE Memory cleanup
	removeAllSuggestions(postalCodeSuggestions);
	removeAllSuggestions(citySuggestions);
}

/*function selectInputFieldText(obj) {
	// TODO only for IE, get better IE detection
	if (document.all) {
		obj.createTextRange();
    	obj.select();
	}
}*/

/**
 * 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;
		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) {
	
		/*if (document.getElementsByTagName) {
			var select = document.getElementsByTagName("select");
			if (select.className == "inputSelectHideable"){
				select.style.visibility = visible ? "visible" : "hidden";
			}
		} else {*/
			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"){
					//if (tempobj.type.substring(0,6) == "select") {
						tempobj.style.visibility = visible ? "visible" : "hidden";
					}
				}
			}
		/*}*/
		
	}
}