/*
 * AutoComplete for CakePHP
 * 
 * @author DualTech Services, Inc
 * @website http://www.dual-tech.com
 * @email DTAutoComplete@ja.spamninja.net
 * @version 0.0.1 (beta)
 */

var debug = 0; // For use with Firebug Debugging

function AutoComplete() {
	var inputValues = new Object();
	var inputBox = null;
	var selectedItem = null;
	var allowAjaxSubmit = true;
	
	/*
	 * Returns input box with focus
	 */
	this.getInputBox = function () {
		return inputBox;
	}
	
	/*
	 * Builds results based on input
	 * 
	 * Takes value from focused input box and determines 
	 * results based on input class and value
	 */
	this.buildResults = function(event) {
		var target = autoComplete.locateTarget(event);
		
		div = document.createElement('div');
		searchRegex = new RegExp(target.value, "i");
		
		type = target.className;
		debugLog('Building results using collection: ' + type);
		
		matches = '<ul class="autoCompleteResultsItems">';
		
		count = 0;
		for(i = 0; i < inputValues[type].length; i++) {
			if (inputValues[type][i].match(searchRegex) != null) {
				matches = matches + '<li onclick="document.getElementById(autoComplete.getInputBox()).value = this.innerHTML.replace(/(<([^>]+)>)/ig,\'\'); document.getElementById(autoComplete.getInputBox()).focus(); ">' + inputValues[type][i].replace(inputValues[type][i].match(searchRegex), '<strong>' + inputValues[type][i].match(searchRegex) + '</strong>') + '</li>';
				count++;
			}
		}
		matches = matches + '</ul>';
		
		div.innerHTML = matches;
		div.id = 'autoCompleteResultsDiv';
		
		if (count > 0 && target.value.length > 0) {
			return div;
		} else {
			return null;
		}
	}
	
	/*
	 * Wrapper for Firebug
	 */
	function debugLog(string) {
		if (this.debug == 1) {
			console.log(string);
		}
	}	

	/*
	 * Registers Input Listeners
	 * 
	 * Will attach listener to all inputs with 
	 * autoComplete="off" set.
	 */
	this.registerInputListeners = function() {
		inputs = document.getElementsByTagName("input");
		
		for(i = 0; i < inputs.length; i++) {
			if (inputs[i].getAttribute('autocomplete') != null) {
				inputs[i].onkeyup = this.handleKeyPress;
				inputs[i].onblur = this.handleBlur;
				inputs[i].onfocus = this.handleFocus;
			}
		}
	}
	
	/*
	 * Handles Key Press in focused autoComplete
	 * input box
	 */
	this.handleKeyPress = function(event) {
		var target = autoComplete.locateTarget(event);
		
		debugLog('Processing Key Press for ' + target.id + '. Value: ' + target.value);
		
		if (!event) {
			var event = window.event;
		}
		if (event.keyCode == 38 && document.getElementById('autoCompleteResultsDiv') != null) { // Key Up
			allowAjaxSubmit = false;
			
			if (selectedItem != null) {
				if (selectedItem >= 1) {
					document.getElementById('autoCompleteResultsDiv').firstChild.childNodes[selectedItem].className = null;
					selectedItem--;
					document.getElementById('autoCompleteResultsDiv').firstChild.childNodes[selectedItem].className = 'selected';
				}
			}
		} else if (event.keyCode == 40 && document.getElementById('autoCompleteResultsDiv') != null){ // Key Down
			allowAjaxSubmit = false;
			
			if (selectedItem != null) {
				if (document.getElementById('autoCompleteResultsDiv').firstChild.childNodes[(selectedItem + 1)] != null) {
					document.getElementById('autoCompleteResultsDiv').firstChild.childNodes[selectedItem].className = null;
					selectedItem++;
					document.getElementById('autoCompleteResultsDiv').firstChild.childNodes[selectedItem].className = 'selected';
				}
			} else {
				selectedItem = 0;
				document.getElementById('autoCompleteResultsDiv').firstChild.childNodes[selectedItem].className = 'selected';
			}
		} else if (event.keyCode == 13 && selectedItem != null) {
			document.getElementById(inputBox).value = document.getElementById('autoCompleteResultsDiv').firstChild.childNodes[selectedItem].innerHTML.replace(/(<([^>]+)>)/ig,'');
			document.getElementById(inputBox).parentNode.removeChild(document.getElementById("autoCompleteResultsDiv"));
			allowAjaxSubmit = true;
		} else {
			results = autoComplete.buildResults(event);
			
			if (results != null) {
				if (document.getElementById('autoCompleteResultsDiv') == null) {
					target.parentNode.appendChild(results);
	
					document.getElementById('autoCompleteResultsDiv').className = 'autoCompleteResults';
					document.getElementById('autoCompleteResultsDiv').style.left = target.offsetLeft + 'px';
				} else {
					document.getElementById('autoCompleteResultsDiv').innerHTML = results.innerHTML;
				}
			} else {
				autoComplete.removeResultsDivIfExists(inputBox);
			}
		}
	}
	
	/*
	 * Checks to make sure AJAX submit is allowed.
	 *  
	 * This is determined based on whether or 
	 * not the user has used the up
	 * and down key to select a value.
	 */
	this.isSubmitAllowed = function() {
		debugLog('Check to see if we allow a AJAX Form Submission...');
		if(allowAjaxSubmit) {
			debugLog('Allowing AJAX to submit.');
			return true;
		} else {
			debugLog('AJAX submit denied!');
			return false;
		}
	}
	
	/*
	 * Handles when input gets focus
	 */
	this.handleFocus = function (event) {
		var target = autoComplete.locateTarget(event);
		
		debugLog('Processing Focus for ' + target.id);
		inputBox = target.id;
		selectionOverride = false;
		selectedItem = null;
	}	
	
	/*
	 * Handles when input loses focus
	 */
	this.handleBlur = function(event) {
		var target = autoComplete.locateTarget(event);
		
		debugLog('Processing Blur for ' + target.id);
		inputId = target.id;
		allowAjaxSubmit = true;
		selectedItem = null;

		setTimeout('autoComplete.removeResultsDivIfExists("' + inputId + '")', 250);
	}
	
	this.removeResultsDivIfExists = function(inputId) {
		if (document.getElementById("autoCompleteResultsDiv") != null) {
			try {
				document.getElementById(inputId).parentNode.removeChild(document.getElementById("autoCompleteResultsDiv"));
			} catch (e) {
				
			}
		}
	}
	
	/*
	 * Function used to add collections to the
	 * list of searched values on key press
	 */
	this.addCollection = function(key, collection) {
		debugLog('[New Collection] ' + key + ': ' + collection);
		inputValues[key] = collection;
	}
	
	/*
	 * Updates the collection for AJAX calls
	 * 
	 * If the value exists in the inputs collection
	 * then it will ignore it. Otherwise, it will
	 * add the value to its collection.
	 */
	this.updateCollection = function() {
		form = document.getElementById(inputBox).form;
           
		for(i = 0; i < form.elements.length; i++) {
			if (form.elements[i].getAttribute('autocomplete') != null) {
				debugLog('Checking for collection update: ' + form.elements[i].className);
				
				isNew = true;
				for(x = 0; x < inputValues[form.elements[i].className].length; x++) {
					if (form.elements[i].value == inputValues[form.elements[i].className][x]) {
						isNew = false;
					}
				}
				
				if (isNew) {
					debugLog('Updated required!');
					inputValues[form.elements[i].className][inputValues[form.elements[i].className].length] = form.elements[i].value;					
				}
			}
		}
	}
	
	this.locateTarget = function(event) {
		if (!event) {
			var event = window.event;
		}
		
		var target;
		
		if (event.target) {
			target = event.target;
		} else if (event.srcElement) {
			target = event.srcElement;
		}
		
		return target;
	}
	
	this.getAttribute = function (element, value) {
		alert(element.type);
		if (element.getAttribute(value)) {
			return element.getAttribute(value);
		} else {
			return element.attributes[value];
		}
	}
}