/* initialize the type-search */
var RESULTS_CONTAINER_HEIGHT = 150;
var NO_RESULTS_CONTAINER_HEIGHT = 35;

var data = [], search_data = [];
var data_len, results_container;
var type_timer = null, type_node, type_form;
var hide_results_timer = null;
var keypress_selected_index = -1;											// remember which result index we're on while the user presses up and down
var arrow_nav = false;														// true if the user is using the arrows to navigate
var halt_type_search = false;
var container_nodes = null;													// cache the container nodes (see get_container_nodes())
var container_node_classes = ["result", "result_hover"];					// class names used for container

var over_results_container = false;
var scrolling_results_container = false;
var over_input = false;

var search_cache = [];
var search_cache_len = 25;
var search_cache_num = 0;

var conn = new AJAXConnection();
conn.onSuccess = find_data;
conn.onError = ajaxError;

/* ajax error reporting */
function ajaxError()
{
	alert("Connection Error: "+ this.error);
}

/* functions for classes that use the type-search */
function TypeSearchDocumentFragment()
{
	var frag = document.createDocumentFragment();
	frag.appendChild( document.createTextNode( this.toString() ) );
	return( frag );
}

/* let the type-search begin */
function type_search( e )
{
	var n, key, blacklist = [9,13,16,17,18,19,20,27,32,33,34,35,36,37,39,45,91,144];
	
	if ( type_timer != null )
		clearTimeout(type_timer);											// if we get here, the user is typing fast. wait until they pause to retrieve data
	
	e = getEvent(e || event);
	key = e.which;
	
	if ( blacklist.in_array(key) )
		return;
	
	n = e.target;
	
	if ( n.value.trim().length == 0 )
	{
		hide_results_container();
		return;
	}
	
	if ( !over_input )
		over_input = n;
	
	if ( type_node && type_node != n )					// user initiated the search from a different textbox
	{
		over_results_container = false;
		hide_results_container();
	}
	
	type_node = n;
	type_form = n.form;

	if ( !halt_type_search )
		type_timer = setTimeout(fetch_data, 250);			// 250ms delay between keystrokes
	else
	{
		e.stopPropagation();
		e.preventDefault();
		halt_type_search = false;
		return( false );
	}
}

function click_search( n )
{
	// double click the input box
	
	if ( type_timer != null )
		clearTimeout(type_timer);						// if we get here, the user is typing fast. wait until they pause to retrieve data
	
	if ( !over_input )
		over_input = n;
	
	if ( type_node && type_node != n )					// user initiated the search from a different textbox
	{
		over_results_container = false;
		hide_results_container();
	}

	type_node = n;
	type_form = n.form;

	type_timer = setTimeout(fetch_data, 250);			// 250ms delay between keystrokes
}

function simulate_search( text )
{
	var i, obj;
	
	text = text.toLowerCase();
	
	for (i = 0; i < search_cache_num; i++)
	{
		if ( search_cache[i].query.toLowerCase() == text )
		{
			obj = {data:search_cache[i].data, search:search_cache[i].query, func:conn.onSuccess};
			obj.func();
			return( true );
		}
	}
	return( false );
}

function search_cached( text )
{
	text = text.toLowerCase();
	
	for (var i = 0; i < search_cache_num; i++)
		if ( search_cache[i].query.toLowerCase() == text )
			return( true );
	
	return( false );
}

function clear_cached_searches()
{
	search_cache_num = 0;
	search_cache = [];
}

function find_data()
{
	var obj = eval(this.data);		// expecting JSON
	var data_len = obj.length;
	
	results_container.innerHTML = "";
	
	if ( obj.length == 0 )
		return;

	if ( obj[0].error == undefined )
	{
		window.search_data = obj;
		
		var i, strlen = this.search.length, val = this.search.toLowerCase();
		var p, gotdata = false;

		if ( strlen == 0 )
			return;
		
		if ( data_len > 0 )
		{
			var frag = document.createDocumentFragment();
			
			if ( results_container.getAttribute("open") == "0" )
			{
				var info = get_node_info( type_node );
				
				results_container.style.display = "block";
				results_container.style.height = "0px";
				results_container.style.overflow = "hidden";
				results_container.style.top = (info.y + info.h + 1) +"px";
				results_container.style.left = info.x +"px";
			}
			for (i = 0; i < data_len; i++)
			{
				/*if ( search_data[i].data.substring(0, strlen).toLowerCase() == val )
				{*/
					gotdata = true;
					
					p = document.createElement("P");
					p.appendChild( obj[i].documentFragment() );
					p.setAttribute("data", obj[i].data);
					p.setAttribute("data_index", i);
					p.onmouseover = rollover_type_search_result;
					p.onmouseout = rolloff_type_search_result;
					p.onclick = click_type_search_result;
					p.className = ( typeof obj[i].className != "undefined" ) ? obj[i].className : "result";
					p.setAttribute("hold_class", p.className);

					frag.appendChild( p );
				//}
			}
			
			if ( !search_cached(this.search) )
			{
				if ( search_cache_num + 1 > search_cache_len )
					search_cache_num = 0;
				
				search_cache[ search_cache_num++ ] = {query:this.search, data:this.data};
			}
			
			if ( gotdata )
			{
				results_container.appendChild( frag );
				
				get_container_nodes(1);
				open_results_container();
			}
		}
	} else
		alert( obj[0].error );
}

function no_search_data()
{
	if ( !results_container || results_container.nodeType != 1 )
		return( false );
	
	results_container.innerHTML = '<p class="nodata">no results match your search.</p>';
	open_results_container('nodata');
}

function open_results_container()
{
	if ( arguments.length == 0 || arguments[0] != 'nodata' )
	{
		if ( results_container.getAttribute("open") == "1" &&
				parseInt(results_container.style.height) >= RESULTS_CONTAINER_HEIGHT
		) return;

		var PX_HOP = 8;
		
		var h = parseInt(results_container.style.height);
		var new_h = h + PX_HOP;
		
		if ( new_h > RESULTS_CONTAINER_HEIGHT )
			new_h = RESULTS_CONTAINER_HEIGHT;

		results_container.style.height = new_h +"px";
		
		if ( new_h < RESULTS_CONTAINER_HEIGHT )
		{
			setTimeout(open_results_container, 10);
			return;
		}
	} else
		results_container.style.height = NO_RESULTS_CONTAINER_HEIGHT +"px";
	
	results_container.style.overflow = "auto";
	results_container.setAttribute("open", "1");
	
	keypress_selected_index = -1;
	
	if ( hide_results_timer != null )
		clearTimeout(hide_results_timer);
	
	hide_results_timer = setTimeout(type_search_mousedown, 3000);
}

function get_container_nodes( recache )
{
	if ( typeof container_nodes == "object" )
	{
		if ( recache )
			container_nodes = [];
		else
			return( container_nodes );
	}
	
	var i, len = results_container.childNodes.length;
	var arr = [];
	
	for (i = 0; i < len; i++)
		if ( container_node_classes.in_array( results_container.childNodes[i].className.split(" ") ) )
			arr.push( results_container.childNodes[i] );
	
	container_nodes = arr;
	return( arr );
}

function is_container_node( ref )
{
	if ( ref == undefined || !ref.nodeType )
		return( false );
	
	var nodes = get_container_nodes();
	var i, len = nodes.length;
	
	for (i = 0; i < len; i++)
		if ( nodes[i] == ref )
			return( i );
	
	return( false );
}

function hide_results_container()
{
	if ( hide_results_timer != null )
	{
		clearTimeout(hide_results_timer);
		hide_results_timer = null;
	}
	if ( results_container ) 
	{
		results_container.style.display = "none";
		results_container.setAttribute("open", "0");
	}
}

// events for each result
function rollover_type_search_result()
{
	arrow_nav = false;
	this.className = "result_hover";
}

function rolloff_type_search_result()
{
	this.className = this.getAttribute("hold_class");
}

function click_type_search_result()
{
	var func = this.getAttribute("clickfunc");
	if ( typeof window[func] == "function" )
		window[func](this);
	else
		type_node.value = this.getAttribute("data");
	
	if ( typeof do_type_search_click == "function" )
		do_type_search_click(this);
}

// events for results container
function rollover_results_container(e)
{
	if ( hide_results_timer != null )
	{
		clearTimeout(hide_results_timer);
		hide_results_timer = null;
	}
	arrow_nav = false;
	over_results_container = this;
}

function rolloff_results_container(e)
{
	if ( hide_results_timer != null )
		clearTimeout(hide_results_timer);
	
	arrow_nav = false;
	over_results_container = false;
	
	hide_results_timer = setTimeout(type_search_mousedown, 5000);
}

function scroll_results_container(e)
{
	if ( hide_results_timer != null )
	{
		clearTimeout(hide_results_timer);
		hide_results_timer = null;
	}
	over_results_container = this;
	scrolling_results_container = true;
}

// events for document and window
function type_search_mousedown(e)
{
	if ( hide_results_timer != null )
		clearTimeout(hide_results_timer);
	
	hide_results_timer = null;
	
	if ( arrow_nav || (!over_results_container && !over_input) )		// if key_selected_index > -1 then we were previously moving up and down through the list, the next mousedown should close the container
		hide_results_container();
	else
		hide_results_timer = setTimeout(type_search_mousedown, 5000);
}

function type_search_mouseup(e)
{
	if ( scrolling_results_container == true )
	{
		if ( hide_results_timer != null )
		{
			clearTimeout(hide_results_timer);
			hide_results_timer = null;
		}
		scroll_results_container = false;
	}
}

function type_search_keypress(e)
{
	if ( typeof results_container == "undefined" || results_container.nodeType != 1 || 
			results_container.getAttribute("open") != "1"
	) {
		halt_type_search = false;
		return;
	}
	
	if ( hide_results_timer != null )
		clearTimeout(hide_results_timer);
	
	var nodes = get_container_nodes();

	e = getEvent(e || event);
	
	arrow_nav = false;
	
	if ( e.which == 38 || e.which == 40 )																						// up / down arrows
	{
		var len = nodes.length - 1;
		var index = keypress_selected_index;
		
		arrow_nav = true;
		halt_type_search = true;
		
		if ( e.which == 38 && keypress_selected_index > 0 )
			index -= 1;
		else if ( e.which == 40 && keypress_selected_index < len )
			index += 1;
		else
			return;
		
		if ( index != keypress_selected_index && keypress_selected_index > -1 )
			nodes[keypress_selected_index].className = nodes[keypress_selected_index].getAttribute("hold_class");
		
		nodes[index].className = "result_hover";
		
		var scroll_range = results_container.scrollTop + RESULTS_CONTAINER_HEIGHT - 10;	// 10 for padding
		if ( !(nodes[index].offsetTop >= results_container.scrollTop && nodes[index].offsetTop <= scroll_range) )
			results_container.scrollTop = nodes[index].offsetTop;
		
		keypress_selected_index = index;
		
		hide_results_timer = setTimeout(type_search_mousedown, 10000);
		
		e.stopPropagation();
		e.preventDefault();
		return( false );
	} else if ( e.which == 13 && keypress_selected_index > -1 ) {																// enter key, select an item
		nodes[keypress_selected_index].onclick();
		
		halt_type_search = true;
		
		e.stopPropagation();
		e.preventDefault();
		return( false );
	} else if ( e.which == 27 && type_node ) {																					// escape key
		halt_type_search = true;
		hide_results_container();
	} else if ( e.which == 37 || e.which == 39 ) {																				// left / right arrowa
		halt_type_search = true;
	} else
		halt_type_search = false;
}

function init_type_search()
{
	results_container = document.createElement("DIV");
	
	results_container.setAttribute("id", "type_search_results");
	results_container.setAttribute("open", "0");
	
	results_container.onmouseover = rollover_results_container;
	results_container.onmouseout = rolloff_results_container;
	results_container.onscroll = scroll_results_container;																		// ie triggers a mouseout when you mouse to the scrollbar, so we track this activity ourselves in order to keep the results open in ie while the user scrolls... lame

	if ( typeof custom_init_type_search == "function" )
		custom_init_type_search( results_container );
	else
		document.body.appendChild( results_container );
}

if ( window.attachEvent )
{
	window.attachEvent("onload", init_type_search);
	document.attachEvent("onmousedown", type_search_mousedown);
	document.attachEvent("onmouseup", type_search_mouseup);
	document.attachEvent("onkeydown", type_search_keypress);
} else {
	window.addEventListener("load", init_type_search, false);
	document.addEventListener("mousedown", type_search_mousedown, false);
	document.addEventListener("mouseup", type_search_mouseup, false);
	document.addEventListener("keydown", type_search_keypress, false);
}