/**
 * Autocomplete
 *
 * <input type="text" name="blah" onfocus="autocomplete_init(this, options, false, true)" autocomplete="off" />
 *
 * Set suggest to true to display an options box with the autocomplete.
 * Set complete to true to automatically complete 
 * Set multiple_values to true to enable autocomplete for multiple comma or semi-colon delimited options (such as email addresses).
 * Note that suggest and multiple_values don't combine (yet, anyway)
 *
 * @author Nick Nettleton / nick@plumdigitalmedia.com
 * @copyright 2005
 * @package UI
 * @version 0.1
 * @todo: Tidy this all up!
 */

/**
 * Initialises the given element with auto-complete features
 */
function autocomplete_init(element, options, suggest, complete, multiple_values) {
	if (navigator.userAgent.toLowerCase().indexOf('safari') !== -1) {
		//return;
	}
	if (suggest) {
		multiple_values = false;
	}

	element.setAttribute('autocomplete', 'OFF');
	var flat_mode = options.length && typeof options[0] === 'string';
	if (flat_mode) {
		options = options.sort();
	} else if (options.length && typeof options[0] === 'object') {
		for (var i in options) {
			if (typeof options[i] === 'object' && options[i].items) {
				options[i].items.sort();
			}
		}
	}
	
	this.old_value = '';

	// override the calling onfocus event
	element.onfocus = function () {
		return true;
	};

	element.onkeydown = function () {
		return true;
	};
	
	element.onkeydown = function (event) {
		var cn, range;
		if (!event) {
			event = window.event; // ie
		}
		
		// in some browsers, the up/down is removing the selection, so put it back
		// we put it in onkeydown to prevent nasty flashing effects
		
		if (suggest && element.autocomplete_menu && element.autocomplete_menu.style.display === 'block') {
			
			cn = element.autocomplete_menu.childNodes[0].childNodes;
			
			// up or down
			if (event.keyCode === 38 || event.keyCode === 40) {
				
				if (element.createTextRange) { // ie
					range = element.createTextRange();
					range.moveStart('character', this.old_value.length);
					range.moveEnd('character', element.value.length);
					range.select();
				} else { // gecko
					element.setSelectionRange(this.old_value.length, element.value.length);
				}
				
				return false;
			}
		}
	};
	
	element.onkeypress = function (event) {
		// this gets called right before onkeyup in firefox, and it what causes a form submit (not the keyup)
		// so, we disbable form submit on return here
		// when the user is hitting return to just choose as item from the menu
		if (event && event.keyCode === 13 && element.suggest_idx !== undefined) {
			return false;
		}
	};

	element.onkeyup = function (event) {
		if (!event) {
			event = window.event; // ie
		}
		
		// in sugest mode, certain keycodes have a special meaning:
		// up and down arrows move through the list, and enter selects the current one
		
		if (suggest && element.autocomplete_menu && element.autocomplete_menu.style.display === 'block') {

			var cn = element.autocomplete_menu.childNodes[0].childNodes;

			// up or down
			if (event.keyCode === 38 || event.keyCode === 40) {

				if (element.suggest_idx !== undefined) {
					if (cn[element.suggest_idx]) {
						cn[element.suggest_idx].style.backgroundColor = 'transparent';
						cn[element.suggest_idx].style.color = '#fff';
					}
				}

				if (element.suggest_idx === undefined) {
					element.suggest_idx = (event.keyCode === 38) ? cn.length - 1 : 1;
				} else {
					element.suggest_idx += (event.keyCode === 38) ? -1 : 1;
				}

				if (element.suggest_idx === cn.length) {
					element.suggest_idx = 0;
				} else if (element.suggest_idx < 0) {
					element.suggest_idx = cn.length - 1;
				}
				
				if (!cn[element.suggest_idx].getAttribute('title')) {
					element.suggest_idx += (event.keyCode === 38) ? -1 : 1;
				}
				
				cn[element.suggest_idx].style.backgroundColor = '#fff';
				cn[element.suggest_idx].style.color = '#003';

				return false;
			}

			// enter (don't forget to return to normal behaviour if there is no list item selected)

			else if (event.keyCode === 13) {
				
				if (element.suggest_idx !== undefined) {
					if (cn[element.suggest_idx]) {
						cn[element.suggest_idx].style.backgroundColor = 'transparent';
						cn[element.suggest_idx].style.color = '#fff';
						if (cn[element.suggest_idx].getAttribute('title')) {
							location.href = cn[element.suggest_idx].getAttribute('title');
							return false;
						}
						element.value = cn[element.suggest_idx].innerHTML;
						element.autocomplete_menu.style.display = 'none';
				
						// put the cursor at the end of the field
						if (element.createTextRange) { // ie
							var range = element.createTextRange();
							// must be a better technique here... ***
							range.moveStart('character', element.value.length);
							range.moveEnd('character', element.value.length);
							range.select();
						} else { // gecko
							element.setSelectionRange(element.value.length, element.value.length);
						}
					}
					element.suggest_idx = undefined;
					
					return false;
				} else if (cn[1]) {
					location.href = cn[1].getAttribute('title');
					return false;
				}
			}

		}
		element.suggest_idx = undefined;

		// the old value

		var old_value = element.value;
		this.old_value = old_value;

		// look for matches

		var matches = [], against;
		if (multiple_values) {
			var tmp	= old_value.split(/[\s,;]+/);
			against	= tmp[tmp.length - 1].toLowerCase();
		} else {
			against	= old_value.toLowerCase();
		}
		if (against) {
			// only find matches if there's something to match against
			for (var i = 0, l = options.length; i < l; i += 1) {
				if (flat_mode) {
					if (options[i] && options[i].toLowerCase().indexOf(against) === 0) {
						matches[matches.length] = options[i];
					}
				} else {
					var group_matches = [];
					for (var j = 0, l2 = options[i].items.length; j < l2; j += 1) {
						if (options[i].items[j] && options[i].items[j].name.toLowerCase().indexOf(against) === 0) {
							group_matches[group_matches.length] = options[i].items[j];
						}
					}
					if (group_matches.length) {
						matches.push({
							group: options[i].group,
							matches: group_matches
						});
					}
				}
			}
		}

		// end if no matches

		if (!matches.length) {
			if (element.autocomplete_menu) {
				element.autocomplete_menu.style.display = 'none';
			}
			return;
		}

		// complete the entry if the match is not perfect (case insensitive)
		
		// there are certain keycode we don't complete for:
		// backspace, delete, cursors, etc.
		// this string _must_ start and end in a;

		var ignore_keys = ';8;46;37;38;39;40;33;34;35;36;45;46;';

		if (complete && flat_mode && matches[0].length !== against.length && ignore_keys.indexOf(';' + event.keyCode + ';') === -1) {
	
			// we don't want to change the case of typed-in stuff, so we append
			// whatever is required of the match to the typed in value
	
			element.value += matches[0].substring(against.length);
	
			// put the cursor back where it belongs, and select the appended text
			// problem in safari here ***

			if (element.createTextRange) { // ie
				var range = element.createTextRange();
				range.moveStart('character', old_value.length);
				range.select();
			} else { // gecko
				element.setSelectionRange(old_value.length, element.value.length);
			}
		}
		
		// display suggestions

		if (suggest) {

			// create autocomplete menu on first run

			if (!element.autocomplete_menu) {
				var el = element.autocomplete_menu = document.createElement('div');
				var n = element;
				var x = element.offsetLeft;
				var y = element.offsetTop + element.offsetHeight;
				// change to use offsetParent - see datepicker ***
				//if(document.all){
					var m = n.offsetParent;
					while (m) {
						x += m.offsetLeft;
						y += m.offsetTop;
						m = m.offsetParent;
					}
				//}
				
				//while(n && n.tagName.toLowerCase() != 'body'){
					// need fix for gecko
				//	x += n.offsetLeft;
				//	y += n.offsetTop;
				//	n = n.parentNode;
				//}
				el.style.position		= 'absolute';
				el.style.left			= (x - 60) + 'px';
				el.style.top			= (y + 2) + 'px';
				el.style.width			= parseInt(element.offsetWidth + 61) - (document.all ? 12 : 2) + 'px';
				el.style.backgroundColor	= '#296CB4';
				el.style.borderTop		= '1px solid #76A1CF';
				el.style.borderLeft		= '1px solid #77A2CF';
				el.style.borderRight	= '1px solid #163A60';
				el.style.borderBottom	= '1px solid #16395F';
				el.style.color			= '#fff';

				document.body.appendChild(el);
				
				var f = function(event)
				{
					setTimeout(function(){
						if(el.style.display != 'none'){ el.style.display = 'none' }
						return true;
					}, 100) ; // give moz a little time for a click event on an menu option to catch up
				};
				//document.attachEvent ? document.attachEvent("onmousedown", f) : 
				//document.addEventListener ? document.addEventListener("mousedown", f, false) :
				//null;
				element.attachEvent ? element.attachEvent("onblur", f) : 
				element.addEventListener ? element.addEventListener("blur", f, false) :
				null;

			}

			// populate the options in the autocomplete menu
			// it's probably better for speed if we can write these to the doc in one go,
			// so we build it into a wrapper

			var ac = element.autocomplete_menu;
			var w = document.createElement('div');
			
			if (flat_mode) {
			
				for (var i = 0, l = matches.length; i < l; i += 1) {
	
					var e			= document.createElement('div');
					e.onmouseout		= function () { this.style.backgroundColor = 'transparent' }
					e.onmouseover	= function () {
						// we can really optimise all this...too much duplication of code***
						if(element.suggest_idx != undefined){
							var cn = element.autocomplete_menu.childNodes[0].childNodes;
							if(cn[element.suggest_idx]){
								cn[element.suggest_idx].style.backgroundColor = 'transparent';
							}
						}
						/*
						// is this _really_ the quickest way to find the index of a childNode?
						// sod it - google has!
						for(var i=0, l=this.parentNode.childNodes.length; i<l; i++){
							if(this.parentNode.childNodes[i] == this){
								element.suggest_idx = i;
								break;
							}
						}
						element.suggest_idx = undefined;
						*/
						this.style.backgroundColor = '#fff';
						this.style.color			= '#003';
					}
					e.onclick		= function (event) { element.value = this.innerHTML; ac.style.display = 'none'; }
					if(document.all) e.style.width = '100%';
					e.style.cursor		= 'pointer';
					e.style.padding		= '2px 5px';
					e.innerHTML		= matches[i];
					w.appendChild(e);
				}
			} else {
				for (var i = 0, l = matches.length; i < l; i += 1) {
					
					var e			= document.createElement('div');
					e.style.padding		= '2px 5px';
					e.style.fontWeight = '700';
					e.style.backgroundColor = '#003';
					e.innerHTML		= matches[i].group;
					w.appendChild(e);
					
					for (var j = 0, l2 = matches[i].matches.length; j < l2; j += 1) {
						var e			= document.createElement('div');
						e.setAttribute('title', matches[i].matches[j].url);
						e.onmouseout	= function(){ this.style.backgroundColor = 'transparent';this.style.color = '#fff'; }
						e.onmouseover	= function(){
							if(element.suggest_idx != undefined){
								var cn = element.autocomplete_menu.childNodes[0].childNodes;
								if(cn[element.suggest_idx]){
									cn[element.suggest_idx].style.backgroundColor = 'transparent';
								}
							}
							this.style.backgroundColor = '#fff';
							this.style.color			= '#003';
						}
						e.onmousedown		= function(event){
							if (this.getAttribute('title')) {
								location.href = this.getAttribute('title');
							}
							element.value = this.innerHTML; 
							ac.style.display = 'none'; 
						}
						if (document.all)  e.style.width = '100%';
						e.style.cursor		= 'pointer';
						e.style.padding		= '2px 5px';
						e.innerHTML		= matches[i].matches[j].name;
						w.appendChild(e);
					}
					
				}
			}

			if (ac.childNodes.length) {
				//ac.childNodes[0].removeNode(true);
				ac.removeChild(ac.childNodes[0]);
			}
			ac.appendChild(w);

			// display the menu if its not already displayed
			if (ac.style.display != 'block') {
				ac.style.display = 'block';
			}
			
			return true;
		}

		// end
		
		return;
	}
}
