/********************************************************

AUTO COMPLETE TEXT FIELD

Is an inclusive app: ie it only works for browsers that are known to handle and display it correctly. We can do this because it enhances the functionality and is not critical to the form working.

It loads a list of options from an XML file.

If the dropdown generates a lot of options there can be problems with certain form elements, like selects, poking through in IE. There are 2 strategies that can be used to circumvent this: 1 is through design (no selects etc below autocomplete), 1 is making sure there are never that many options (by setting it to autocomplete only if 1 or more letters are typed, or even 2 or more), or by hiding the selects on expand.

These can be set in the config object.


Credits
---------

Based on the solution described in the book 
The Javascript Anthologyv by Cameron Adams and James Edwards

XMLHttpRequest part based on solution in the book
Javascript The definitive Guide by David Flanagan (5th Edition)

Requires (usually foundl in main.js)
-----------

	addLoadHandler
	addStyleSheet
	identifyBrowser
	attachEventListener
	getEventTarget
	DOMbuilder
	stopDefaultAction
	getLeft
	getTop


********************************************************/

addLoadHandler(initAutoComplete);

var config = {}
config.xmlLocation = '/flight-information/flight-search/tools/auto-complete.aspx';
config.hideSelects = false;
config.lettersTyped = 1;
config.acClass = 'place';
config.acTextField;

var acControlDiv, destinations;

/* 
	Includes known browsers, fails silently for browsers not in the list
	Add stylesheet for autocomplete, if the browser passes the test
	Fires off XMLhttpRequest to get destinations from XML file generated by SiteCore
*/
function initAutoComplete() {
	var userAgent = identifyBrowser();
	
	// check XMLHttpRequest
	var requester;
	if ( typeof XMLHttpRequest != "undefined" ) {
		requester = new XMLHttpRequest();
	} else if ( typeof ActiveXObject != 'undefined' ) {
		requester = new ActiveXObject('Microsoft.XMLHTTP');
	} else {
		requester = undefined;
	}
	
	// check browser
	var supported = false;
	var supportedBrowsers = 	[
								'ie6',
								'safari2',
								'ie5.5',
								'ie6',
								'ie7',
								'mozilla'
							];
	
	var len = supportedBrowsers.length;
	for ( var i = 0; i < len; i++ ) {
		if ( supportedBrowsers[i] == userAgent ) {
			supported = true;
			break;
		}
	}
	
	if(supported && typeof requester != 'undefined') {
		// attach autocomplete stylesheet
		addStyleSheet('/css/autocomplete.css');
		
		// find the textfield it will be autocompleting
		// Note that it will find only the first input element with the classname specified.
		var inputs = document.getElementsByTagName('input');
		var len = inputs.length;
		for (var i = 0; i < len; i++) {
			var currInput = inputs[i];
			if ( currInput.className == config.acClass ) {
				config.acTextField = currInput;
			}
		}
		
		// create 'control' div
		acControlDiv = document.getElementsByTagName('body')[0].appendChild(DIV({'class':'autoCompleteControl'}));
		
		// clean up circular references on unload
		addUnloadHandler(acCleanUp);
		
		// set up xml event listener
		requester.onreadystatechange = function () {
			if ( requester.readyState == 4 ) { // 4 means server's response is complete
				if ( requester.status == 200 ) {
					translateXML(requester.responseXML);
				}
			}
		}
	
		// send requester
		requester.open('GET', config.xmlLocation);
		requester.send(null);
		
	}
}

// takes destinations xml and transforms into array
function translateXML(destXML) {
	destinations = [];
	var xmlRows = destXML.getElementsByTagName('d');
	var len = xmlRows.length;
	for(var i = 0; i < len; i++) {
		destinations[i] = xmlRows[i].firstChild.data;
	}
	
	createAutoComplete();
}


function createAutoComplete() {
	// disable native browser autocompleting features
	config.acTextField.setAttribute("autocomplete", "off");
	attachEventListener(config.acTextField, "keydown", keydownAutoComplete, false);
	attachEventListener(config.acTextField, "blur", blurAutoComplete, false);
	
	return true;
}

					 
function keydownAutoComplete(event) {
	
	if (typeof event == "undefined" ) {
		event = window.event;
	}
	
	switch (event.keyCode) {
		case 9:			// tab
		case 13:		// enter
		case 16:		// shift
		case 17:		// ctrl
		case 18:		// alt
		case 20:		// caps lock
		case 33:		// page up
		case 34:		// page down
		case 35:		// end
		case 36:		// home
		case 37:		// left arrow
		case 39:		// right arrow
		break;
		
		case 27:		// esc
			closeDropdown();
			break;
			
		case 38:		// up arrow
		
			var target = getEventTarget(event);
			var autoCompleteDropdown = document.getElementById("autoCompleteDropdown");
			
			if (autoCompleteDropdown != null ) {
				var childLis = autoCompleteDropdown.childNodes;
				var selected = false;
				
				var len = childLis.length;
				for ( var i = 0; i < len; i++ ) {
					var currChild = childLis[i];
					if ( currChild.className == 'hover' ) {
						selected = true;
					
						if ( i > 0 ) {
							currChild.className = "";
							childLis[i-1].className = "hover";
							
							target.value = childLis[i - 1].firstChild.nodeValue;
						}
						
						break;
					}
				} // end loop
						
				if (!selected) {
					childLis[0].className = "hover";
					target.value = childLis[0].firstChild.nodeValue;
				} 
			}
			
			stopDefaultAction(event);
			break; // end case 38
		
		case 40: 		// down arrow
		
			var target = getEventTarget(event);
			var autoCompleteDropdown = document.getElementById("autoCompleteDropdown");
			
			if (autoCompleteDropdown != null ) {
				var childLis = autoCompleteDropdown.childNodes;
				var selected = false;
				
				var len = childLis.length;
				
				for ( var i = 0; i < len; i++ ) {
					var currChild = childLis[i];
					if ( currChild.className == 'hover' ) {
						selected = true;
						// alert(currChild.firstChild.nodeValue);
					
						if ( i < len - 1 ) {
							currChild.className = "";
							childLis[i+1].className = "hover";
							
							target.value = childLis[i + 1].firstChild.nodeValue;
						}
						
						break;
					}
				}	// end loop
			 
			
				if (!selected) {
					childLis[0].className = "hover";
					target.value = childLis[0].firstChild.nodeValue;
				}
			}
			
			stopDefaultAction(event);
			break; // end case 38
		
		case 8:			// backspace
		case 46: 		// delete
		
			if ( typeof autoCompleteTimer != "undefined" ) {
				clearTimeout(autoCompleteTimer);
			}
			
			autoCompleteTimer = setTimeout ("generateDropdown(false)", 500);
			break;
		
		default:
		
			if ( typeof autoCompleteTimer != "undefined" ) {
				clearTimeout(autoCompleteTimer);
			}
			
			var target = getEventTarget(event);
			
			var inputRanges = "false";
			/* Uncomment this if you want it to automatically autocomplete as you're typing
			if ( typeof target.createTextRange != "undefined" || typeof target.setSelectionRange != "undefined" ) {
				inputRanges = "true";
			}
			*/
			autoCompleteTimer = setTimeout( "generateDropdown(" + inputRanges + ")", 500);
		
	} // end switch

	return true;
}

function generateDropdown(doAutoComplete) {
	closeDropdown();
	
	// only show drop down if there is at least one character in the textfield
	if ( config.acTextField.value.length >= config.lettersTyped ) {
		var acUl = UL({'id':'autoCompleteDropdown'});
		acUl.autoCompleteInput = config.acTextField;
		
		var len = destinations.length;
		for ( var i = 0 ; i < len; i++ ) {
			var currDest = destinations[i]; 
			if ( currDest.toLowerCase().indexOf(config.acTextField.value.toLowerCase()) == 0 ) {
				
				var newLi = document.createElement("li");
				newLi.appendChild( document.createTextNode( currDest ) );
				
				attachEventListener( newLi, "mouseover", mouseoverDropdown, false );
				attachEventListener( newLi, "mouseout", mouseoutDropdown, false );
				attachEventListener( newLi, "mousedown", mousedownDropdown, false );
				
				acUl.appendChild( newLi );
			}
		}
		
		if ( acUl.firstChild != null ) {
			acControlDiv.appendChild(acUl);
			// position element
			acControlDiv.style.top = getTop(config.acTextField) + (config.acTextField.offsetHeight ) + "px";
			acControlDiv.style.left = getLeft(config.acTextField) + "px";
			// document.getElementsByTagName( "body" )[0].appendChild(acUl);
		}
		
		if ( typeof doAutoComplete != "undefined" && doAutoComplete ) {
			autoComplete();
		}
	}
	
	// hide select boxes so that they don't come poking through
	if(config.hideSelects) {
		showSelects(false); 
	}
	
	return true;	
}

function showSelects(show) {
	var displayProp = (show) ? 'visible' : 'hidden';

	var selects = document.getElementsByTagName('select');
	
	var len = selects.length;
	for (var i = 0; i < len; i++ ) {
		selects[i].style.visibility = displayProp;
	}
}

function blurAutoComplete() {
  if (typeof autoCompleteTimer != "undefined") {
    clearTimeout(autoCompleteTimer);
  }

  closeDropdown();

  return true;
}

function closeDropdown() {
	var autoCompleteDropdown = document.getElementById("autoCompleteDropdown");
	
	if ( autoCompleteDropdown != null ) {
		// remove event listeners
		var listItems = autoCompleteDropdown.getElementsByTagName('li');
		var len = listItems.length;
		for( var i = 0 ; i < len; i++) {
			var currListItem = listItems[i];
			detachEventListener( currListItem, "mouseover", mouseoverDropdown, false );
			detachEventListener( currListItem, "mouseout", mouseoutDropdown, false );
			detachEventListener( currListItem, "mousedown", mousedownDropdown, false );
		}
		
		
		autoCompleteDropdown.parentNode.removeChild(autoCompleteDropdown);
	}
	
	// show selects again
	if(config.hideSelects) {
		showSelects(true);
	}
	
	return true;
}

function mouseoverDropdown(event) {
	if ( typeof event == "undefined" ) {
		event = window.event;
	}
	
	var target = getEventTarget(event);
	
	while( target.nodeName.toLowerCase() != "li" ) {
		target = target.parentNode;
	}
	
	var childLis = target.parentNode.childNodes;
	
	var len = childLis.length;
	for (var i = 0; i < len; i++ ) {
		childLis[i].className = "";
	}
	
	target.className = "hover";
	return true;
}

function mouseoutDropdown(event) {
	if ( typeof event == "undefined" ) {
		event = window.event;
	}
	
	var target = getEventTarget(event);
	
	while ( target.nodeName.toLowerCase() != "li" ) {
		target = target.parentNode;
	}
	
	target.className = "";
	return true;
}

function mousedownDropdown(event) {
	if ( typeof event == "undefined" ) {
		event = window.event;
	}
	
	var target = getEventTarget(event);
	
	while ( target.nodeName.toLowerCase() != "li" ) {
		target = target.parentNode;
	}
	
	target.parentNode.autoCompleteInput.value = target.firstChild.nodeValue;
	
	closeDropdown();
	
	return true;
}

function autoComplete() {
	var cursorMidway = false;
	
	if ( typeof document.selection != "undefined" ) {
		var range = document.selection.createRange();
		
		if (range.move("character", 1) != 0 ) {
			cursorMidway = true;
		}
	} else if ( typeof config.acTextField.selectionStart != "undefined" && config.acTextField.selectionStart < config.acTextField.value.length ) {
		cursorMidway = true;
	}
	
	var originalValue = config.acTextField.value;
	var autoCompleteDropdown = document.getElementById("autoCompleteDropdown");
	
	if ( autoCompleteDropdown != null && !cursorMidway ) {
		autoCompleteDropdown.firstChild.className = "hover";
		config.acTextField.value = autoCompleteDropdown.firstChild.firstChild.nodeValue;
		// IE
		if ( typeof config.acTextField.createTextRange != "undefined" ) {
			var range = config.acTextField.createTextRange();
			range.moveStart("character", originalValue.length);
			range.select();
		// other browsers
		} else if ( typeof config.acTextField.setSelectionRange !=undefined ) {
			config.acTextField.setSelectionRange(originalValue.length, config.acTextField.value.length);
		}
		
		if ( autoCompleteDropdown.childNodes.length == 1 ) {
			setTimeout("closeDropdown();", 10);
		}
	}	
	return true;	
}

function acCleanUp() {
	// remove event listeners
	detachEventListener(config.acTextField, "keydown", keydownAutoComplete, false);
	detachEventListener(config.acTextField, "blur", blurAutoComplete, false);
	closeDropdown();
	document.getElementsByTagName('body')[0].removeChild(acControlDiv);
	config = null;
	destinations = null;
	return true;
}

