function Suggestion(input, action, tpl, ar, itemClicked, allow) {
	/*
	input = textbox to apply suggestion to
	action = xml suggestion list page
	tpl = item template
	ar = suggestion argument list
		e.g. tag.xml.php?q=search_str
		<tag><name>text</name><freq>#number</freq></tag> use {item: 'tag',title:'name',count:'freq',query:'q'}
	itemClicked = fire when click on item
	allow = allowed characters set
	*/
	input = $(input);
	tpl = tpl ? new Template(tpl) : new Template('<span style="float:right">#{count}</span><span class="value">#{title}</span>');
	itemClicked =itemClicked ? itemClicked : Prototype.emptyFunction;
	var arg = $H({item: 'item', title: 'title', count: 'count', query: 'q'});
	if(ar) {
		arg.update(ar);
	}
	var hasSuggestion = false;
	var typing = true;
	var suggest = $(Builder.node('div',{ className:'suggest', style: 'display: none'}))
	input.insert({after: suggest});
	var items = [];
	var values = [];
	var pos = -1;
	var old = '';
	var focused = false;
	input.observe('focus',function() { focused = true; });
	input.observe('blur',function() { focused = false; });
	new Form.Element.Observer(input, 0.5, function(el, value) {
		if(typing&&focused) {
			old = value;
			value = allow ? value.gsub(new RegExp('[^'+allow+']'),'') : value;
			clear();
			new Ajax.Request(action, {
				onComplete: function(output) {
					if(output.responseText) {
						hasSuggestion = true;
						var data = new XMLData(output.responseXML, arg.get('item'));
						var n = data.length;
						for(var i=0; i<n; i++) {
							addItem(data.getData(i,arg.get('title')),data.getData(i,arg.get('count')));
						}
						if(data.getData(0,arg.get('title')) == value)
							hilight(0);
						show();
					}
				},
				parameters: arg.get('query')+'='+value,
				method: 'get'
			});
		}
	});

	input.onkeydown = function(e) {
		if(hasSuggestion) {
			var key = Prototype.Browser.IE ? event.keyCode : e.keyCode;
			if(key==Event.KEY_DOWN) {
				if(!suggest.visible())
					show();
				else
					hilight(pos+1);
			} else if(key==Event.KEY_UP) {
				if(!suggest.visible())
					show();
				else
					hilight(pos-1);
			} else {
				if(key==Event.KEY_ESC)
					input.value = old;
				else if(key==Event.KEY_TAB)
					suggest.hide();
				resumeTyping();
				if(key==Event.KEY_RETURN) {
					itemClicked();
				} else {
					return true;
				}
			}
			
			return false;
		}
	};
	this.clear = clear;

	function addItem(title, count) {
		var i, p=items.length;
		suggest.insert(i = $(Builder.node('div',{className: 'item'})));
		i.update(tpl.evaluate({title: title, count: count}));
		i.observe('mouseover',function() { hilight(p, true); });
		i.observe('mousedown', function() {hilight(p); resumeTyping(); itemClicked(); clear();});
		items.push(i);
		values.push(title);
	}
	function show() {
		var offset = input.cumulativeOffset();
		x = offset.left;
		y = offset.top + input.getHeight();
		suggest.setStyle({left: x+'px', top: y+'px'});
		suggest.show();
		Event.observe(document.body,'mousedown',observeClick);
		var bottom = y + suggest.getHeight();
		var scrollTop = document.viewport.getScrollOffsets().top;
		var d = bottom - scrollTop - document.viewport.getHeight();
		if(d > 0)
			window.scrollBy(0,d);
	}
	function hide() {
		Event.stopObserving(document.body,'mousedown',observeClick);
		suggest.hide();
	}
	function hilight(x,only) {
		if(pos>-1)
			items[pos].removeClassName('hilight');
		if(x==items.length || x==-1) {
			pos = -1;
			input.value = old;
			return;
		} else if(x<-1)
			x = items.length -1;
		pos = x;
		items[pos].addClassName('hilight');
		typing = false;
		if(!only) //if only == true then hilight only
			input.value = values[pos];
		//Event.observe(document.body,'mousedown',observeClick);
	}
	function clear() {
		hide();
		suggest.update();
		items.clear();
		values.clear();
		pos = -1;
		hasSuggestion = false;
	}
	function resumeTyping() {
		//Event.stopObserving(document.body,'mousedown',observeClick);
		typing = true;
		//suggest.hide();
	}
	function observeClick(e) { 
		if(e.element()==input || e.findElement('div').hasClassName('item')) {
		} else {
			//input.value = old;
			hide();
		}
		resumeTyping();
	}
}
