var _cur_input_object=null;
var _cur_storage=null;
var _suggestion_funcs=[];
var _old_onblur=null;
var _suggestion_disabled=false;
var _cur_suggestions=null;
var _old_completable=null;
var _sugg_selected_ix=-1;

var _user_agent_parts={};

var ESC_KEYCODE = 27;
var UP_KEYCODE = 38;
var DOWN_KEYCODE = 40;
var ENTER_KEYCODE = 13;
var TAB_KEYCODE = 9;
var LEFT_KEYCODE = 37;
var RIGHT_KEYCODE = 39;
var COMMA_KEYCODE_EX=",".charCodeAt(0);

var _display_error_enabled=false;

function _auto_complete_setup() {
	_atach_event(document.body,"onkeydown",_handle_keyaction);
	_atach_event(document.body,"onkeypress",_handle_keyaction);
}

function _auto_complete_suggestion_add(fn) {
	for(var i=_suggestion_funcs.length;--i>=0;) {
		if(_suggestion_funcs[i]===fn) {
			return;
		}
	}
	_suggestion_funcs.push(fn);
}

function _atach_event(obj,event_name,handler) {
	var old_handler=obj[event_name];
	if(!old_handler){
		obj[event_name]=handler
	}else{
		obj[event_name]=function() {
			var res_old=old_handler.apply(this,arguments),res_new=handler.apply(this,arguments);
			return !(res_old===false||res_new===false);
		}
	}
	return old_handler
}

function _handle_keyaction(evnt) {
	evnt=evnt||window.event;
	var obj=evnt.target||evnt.srcElement;
	if ("INPUT"==obj.tagName&&obj.type.match(/^text$/i)||"TEXTAREA"==obj.tagName) {
		var key_code=_get_key_code(evnt);
		var evnt_key_down=evnt.type=="keydown";
		var shift_key=evnt.shiftKey;
		if (obj!==_cur_input_object||_cur_storage===null) {
			_cur_input_object=obj;
			var founded=false;
			if (ENTER_KEYCODE!==key_code&&ESC_KEYCODE!==key_code) {
				for (var i=0;i<_suggestion_funcs.length;++i) {
					var storage=_suggestion_funcs[i](obj,evnt);
					if (storage) {
						_cur_storage=storage;
						_old_onblur=_atach_event(_cur_input_object,"onblur",_auto_complete_clear);
						founded=true;
						break;
					}
				}
				if (!founded) {
					_cur_input_object=null;
					_auto_complete_clear(null);
				}
			}
		}
		if (_cur_storage) {
			var is_enter_completed=_cur_storage.is_key_action_completed(key_code,evnt_key_down,shift_key);
			var sugg_count=_cur_suggestions&&_cur_suggestions.length>0;
			var suggestion_exists=false;
			if (is_enter_completed&&sugg_count) {
				suggestion_exists=!_suggestion_disabled&&!(!_cur_suggestions);
				window.setTimeout(
					function() {
						if (_cur_storage) {
							_auto_complete_key_action(key_code,evnt_key_down,shift_key)
						}
					}
				,0)
			} else if (!is_enter_completed) {
				suggestion_exists=_ac_isCompleteListShowing()&&(key_code==ESC_KEYCODE||!shift_key&&key_code==DOWN_KEYCODE||!shift_key&&key_code==UP_KEYCODE);
				window.setTimeout(
					function() {
						if (_cur_storage) {
							_auto_complete_key_action(key_code,evnt_key_down,shift_key)
						}
					}
				,0)
			} else {
				if (_cur_storage.oncomplete) {
					_cur_storage.oncomplete(false,key_code,_cur_input_object,undefined)
				}
			}
			if (suggestion_exists) {
				_event_handled_and_completed(evnt);
			}
			return!suggestion_exists;
		}
	}
	return true
}

function _get_key_code(evnt) {
	var key_code;
	if(evnt.keyCode) {
		key_code=evnt.keyCode
	} else if(evnt.which) {
		key_code=evnt.which
	}
	return key_code
}

function _is_defined(val) {
	return typeof val!="undefined"
}

function _auto_complete_clear(evnt){
	window.setTimeout(function(){
		if(_cur_input_object){
			_cur_input_object.onblur=_old_onblur;
		}
		_cur_storage=null;
		_cur_input_object=null;
		_old_onblur=null;
		_suggestion_disabled=false;
		_auto_complete_show(false);
	},0);
}

function _auto_complete_key_action(key_code,evnt_key_down,shift_key) {
	var is_cursor_move=key_code===LEFT_KEYCODE||key_code===RIGHT_KEYCODE;
	var at_end_of_text=_input_get_cursor_position(_cur_input_object)===_cur_input_object.value.length;
	if (!is_cursor_move||_ac_isCompleteListShowing()||key_code===RIGHT_KEYCODE&&at_end_of_text){
		_get_suggestions(function(){_auto_complete_key_action2(key_code,evnt_key_down,shift_key)});
	} else {
		_auto_complete_key_action2(key_code,evnt_key_down,shift_key);
	}
}
function _auto_complete_key_action2(key_code,evnt_key_down,shift_key) {
	var show=true;
	var sugg_cnt=_cur_suggestions?_cur_suggestions.length:0;
	if (_cur_storage.is_key_action_completed(key_code,evnt_key_down,shift_key)) {
		if (_sugg_selected_ix<0&&sugg_cnt>=1) {
			_sugg_selected_ix=0;
		}
		if (_sugg_selected_ix>=0) {
        	var i=_cur_suggestions[_sugg_selected_ix].value;
			_complete_input();
			if (_cur_storage.oncomplete) {
				_cur_storage.oncomplete(true,key_code,_cur_input_object,i);
			}
		}
	}else{
		switch(key_code) {
			case ESC_KEYCODE:
				_sugg_selected_ix=-1;
				show=false;
				break;
			case UP_KEYCODE:
				if (evnt_key_down) {
					_sugg_selected_ix=Math.max(sugg_cnt>=0?0:-1,_sugg_selected_ix-1);
				}
				break;
			case DOWN_KEYCODE:
				if (evnt_key_down) {
					_sugg_selected_ix=Math.min(sugg_cnt-1,_sugg_selected_ix+1);
				}
				break;
		}
	}
	if (_cur_input_object) {
		_auto_complete_show(show,_cur_storage.f)
	}
}

function _event_handled_and_completed(evnt){
	if ("stopPropagation"in evnt) {
		evnt.stopPropagation();
	} else {
		evnt.cancelBubble=true;
	}
	if ("preventDefault"in evnt) {
		evnt.preventDefault()
	}
}

function _auto_complete_show(visible,b){
	if(b===undefined){
		b=1
	}
	var list_ctrl=document.getElementById("autocompleteBox");
	if (visible&&_cur_suggestions&&_cur_suggestions.length) {
		if(!list_ctrl){
			list_ctrl=document.createElement("DIV");
			list_ctrl.id="autocompleteBox";
			list_ctrl.style.position="absolute";
			list_ctrl.style.display="none";
			document.body.appendChild(list_ctrl);
		}
		if(_sugg_selected_ix<0){
			_sugg_selected_ix=0;
		}
		var sugg_el_html=[];
		for(var i=0;i<_cur_suggestions.length;++i){
			sugg_el_html.push('<div onmousedown="try{_ac_select(',i,')}finally{return false}"',i==_sugg_selected_ix?" class=\"selected\">":(i==0?" class=\"first_item\" >":" >"),_cur_suggestions[i].html,"</div>")
		}
		list_ctrl.innerHTML=sugg_el_html.join("");
		var fb=_get_frame_box(_cur_input_object);
		list_ctrl.style.left=fb.x+"px";
		list_ctrl.style.top=fb.y+fb.h+"px";
		list_ctrl.style.display="";
		var fbl=_get_frame_box(list_ctrl);
		if (b&1) {
			var wh=_get_window_metric(window,_height_obj);
			if(fbl.y+fbl.h>wh){
				//list_ctrl.style.top=fb.y-fbl.h+"px"; // If autoplacement needed - remove commemt's mark from the line's begin
			}
			var ww=_get_window_metric(window,_width_obj);
			if(fbl.x+fbl.w>ww){
				list_ctrl.style.left=fb.x+fb.w-fbl.w+"px";
			}
		}
	} else {
		if (list_ctrl) {
			list_ctrl.style.display="none";
			list_ctrl.innerHTML=""
		}
	}
}

Function.prototype.inherits=function(parent_cls){
	var new_cls=function(){};
	new_cls.prototype=parent_cls.prototype;
	this.i=parent_cls.prototype;
	this.prototype=new new_cls;
};

function _AC_Store(){}
_AC_Store.prototype.completable=function(a,b){
	alert("UNIMPLEMENTED completable")
};
_AC_Store.prototype.completions=function(a,b){
	alert("UNIMPLEMENTED completions")
};
_AC_Store.prototype.oncomplete=function(a,b,c,d){
};
_AC_Store.prototype.substitute=function(a,b,c,d){
	alert("UNIMPLEMENTED substitute")
};
_AC_Store.prototype.g=true;
_AC_Store.prototype.f=1;
_AC_Store.prototype.is_key_action_completed=function(key_code,evnt_key_down,shift_key) {
	if(!evnt_key_down&&(ENTER_KEYCODE===key_code||COMMA_KEYCODE_EX==key_code&&this.g)) {
		return true;
	}
	if(TAB_KEYCODE===key_code&&!shift_key){
		return evnt_key_down==_is_IE();
	}
	return false;
};

function _AC_SimpleStore(arr,rq_handler) {
	this.d={};
	this.tm=false;
    this.rq_handler=rq_handler;
    for (var i=0;i<arr.length;++i) {
		var el=arr[i];
		if (!el) { continue }
		var d=el.split(/[^A-Za-z0-9_А-Яа-я\-]+/); // было /\W+/
		for (var e=0;e<d.length;++e) {
			if (d[e]) {
				var f=d[e].charAt(0).toLowerCase();
				var g=this.d[f];
				if (!g) {
					g=(this.d[f]=[]);
				} else if (g[g.length-1].value==el) {
					continue;
				}
				g.push(new _AC_Completion(el,null));
			}
		}
	}
	this.countThreshold=10;
	_AC_Store.call(this);
}
_AC_SimpleStore.inherits(_AC_Store);
_AC_SimpleStore.prototype.reload=function(find,fn) {
	//ajax
	if (this.tm) {
		clearTimeout(this.tm);
		this.tm=false;
	}
	if (find.length<1) {
		//this_cpy.assign_new([]);
		if (fn) fn();
		return;
	}
	var this_cpy = this;
	this.tm = window.setTimeout(
		function () {
            this_cpy.rq_handler(
                        find,
                        function(arr) {
                            this_cpy.assign_new(arr);
                            if (fn) fn();
                        }
                    );
            /*
            window.setTimeout(
				function() {
					this_cpy.assign_new(['pizza','pizza mega','simple','simple plan','test','test mandarin']);
					if (fn) fn();
				}
				,2*1000);
			*/
		}
		,1000);

}
_AC_SimpleStore.prototype.assign_new=function(arr) {
	var n_g={};
	for (var i=0;i<arr.length;++i) {
		var el=arr[i];
        if (!el) { continue }
		var words=el.split(/[^A-Za-z0-9_А-Яа-я\-]+/); // было /\W+/
        for (var wIx=0;wIx<words.length;++wIx) {
			if (words[wIx]) {
				var first_char=words[wIx].charAt(0).toLowerCase();
				var arr_by_char=n_g[first_char];
				if (!arr_by_char) {
					arr_by_char=(n_g[first_char]=[]);
				} else if (arr_by_char[arr_by_char.length-1].value==el) {
					continue;
				}
				arr_by_char.push(new _AC_Completion(el,null));
			}
		}
	}
	this.d=n_g;
}
_AC_SimpleStore.prototype.oncomplete=function(a,b,c,d){
	if (this.tm) {
		clearTimeout(this.tm);
		this.tm=false;
	}
};
_AC_SimpleStore.prototype.completable=function(a,b) {
	var c=0,d=0;
	for(var e=0;e<b;++e) {
		var f=a.charAt(e);
		switch(d) {
			case 0:
				if('"'==f) {
					d=1
				} else if ( !( (f>='A'&&f<='Z') || (f>='a'&&f<='z') || (f>='А'&&f<='Я') || (f>='а'&&f<='я') || (f>='0'&&f<='9') || f=="-" || f=="_" ) ) { // if(","==f) {
					c=e+1
				}
				break;
			case 1:
				if('"'==f) {
					d=0
				}
				break
		}
	}
	while(c<b&&" \t\r\n".indexOf(a.charAt(c))>=0){++c}
	return a.substring(c,b)
};

_AC_SimpleStore.prototype.completions=function(a,b){
	if(!a){
		return[];
	}
	var c=new RegExp("^(.*[\\s<\"',])?("+a.replace(/([\^*+\-\$\\\{\}\(\)\[\]\#?\.])/g,"\\$1")+")(.*)","i");
	if(!(b&&b.length)&&a){
		b=this.d[a.charAt(0).toLowerCase()];
	}
	var d=[];
	if(b){
		for(var e=0;e<b.length;++e){
			var f=b[e].value.match(c);
			if(f){
				d.push(new _AC_Completion(b[e].value,_escape_special_chars(f[1]||"")+"<strong>"+_escape_special_chars(f[2])+"</strong>"+_escape_special_chars(f[3])));
				if (d.length>this.countThreshold) {
					break;
				}
			}
		}
	}
	d.sort(_AC_CompareACCompletion);
	return d;
};
function _AC_CompareACCompletion(val1,val2) {
	if(val1.value===val2.value) return 0;
	return (val1.value.toLowerCase().replace(/^[^A-Za-z0-9_А-Яа-я\-]*/,"") < val2.value.toLowerCase().replace(/^[^A-Za-z0-9_А-Яа-я\-]*/,""))?-1:1; // было \W
}
_AC_SimpleStore.prototype.substitute=function(str_orig,curs_pos,str_completable,suggestion){
	return str_orig.substring(0,curs_pos-str_completable.length)+suggestion.value+", "+str_orig.substring(curs_pos);
};

function _AC_Completion(a,b){
	this.value=a;
	this.html=b
}
_AC_Completion.prototype.toString=function(){
	return"(AC_Completion: "+this.value+")";
};


function _is_IE() {
	return _is_user_agent_part("msie")&&!window.opera;
}

function _is_user_agent_part(agent_part) {
	if(agent_part in _user_agent_parts) {
		return _user_agent_parts[agent_part];
	}
	return _user_agent_parts[agent_part]=navigator.userAgent.toLowerCase().indexOf(agent_part)!=-1;
}

function _input_get_cursor_position(input_obj) {
	if ("INPUT"==input_obj.tagName) {
		var pos=input_obj.value.length;
		if (undefined!=input_obj.selectionStart) {
			pos=input_obj.selectionStart
		} else if (document.selection) {
			var sr=document.selection.createRange();
			sr.moveStart("character",-pos);
			pos=sr.text.length;
		}
		return pos
	} else {
		return _input_get_cursor_position_custom(window,input_obj);
	}
}

function _input_get_cursor_position_custom(wnd,input_obj) {
	try{
		if(_is_defined(input_obj.selectionEnd)) {
			return input_obj.selectionEnd
		} else if(wnd.document.selection&&wnd.document.selection.createRange) {
			var sr=wnd.document.selection.createRange();
			if (sr.parentElement()!=input_obj) {return-1}
			var srd=sr.duplicate();
			srd.moveToElementText(input_obj);
			srd.setEndPoint("EndToStart",sr);
			var pos=srd.text.length;
			if(pos>input_obj.value.length) {return-1}
			return pos;
		} else {
			_display_error_silent("Unable to get cursor position for: "+navigator.userAgent);
			return input_obj.value.length;
		}
	} catch(e) {
		_trace_exception(e,"Cannot get cursor pos");
	}
	return-1
}

function _move_cursor_to_end(wnd,input_object,end_pos) {
	if(_is_defined(input_object.selectionEnd)&&_is_defined(input_object.selectionStart)) {
		input_object.selectionStart=end_pos;
		input_object.selectionEnd=end_pos;
	} else if(wnd.document.selection&&input_object.createTextRange) {
		var sr=input_object.createTextRange();
		sr.collapse(true);
		sr.move("character",end_pos);
		sr.select();
	}
}

function _get_suggestions(fn) {
	if (!_suggestion_disabled) {
		var pos=_input_get_cursor_position(_cur_input_object);
		var _new_completable=_cur_storage.completable(_cur_input_object.value,pos);
		if (_new_completable==_old_completable) {
			if (fn) fn();
			return;
		}
		var c;
		_cur_suggestions=null;
		_sugg_selected_ix=-1;
		// //var old_sugg=_sugg_selected_ix>=0?_cur_suggestions[_sugg_selected_ix].value:null;
		//_cur_suggestions=_cur_storage.completions(_new_completable,c);
		// //_sugg_selected_ix=-1;
		// //for(var i=0;i<_cur_suggestions.length;++i) {
		// //	if(old_sugg==_cur_suggestions[i].value) {
		// //		_sugg_selected_ix=i;
		// //		break;
		// //	}
		// //}
		_old_completable=_new_completable;
		_cur_storage.reload(
				_new_completable,
				function(){
					_cur_suggestions=_cur_storage.completions(_new_completable,c);
					if (fn) fn();
				}
			);
		return;
	}
	_old_completable=null;
	_cur_suggestions=null;
	_sugg_selected_ix=-1;
	if (fn) fn();
}

function _complete_input() {
	var pos=_input_get_cursor_position(_cur_input_object);
	_cur_input_object.value=_cur_storage.substitute(_cur_input_object.value,pos,_old_completable,_cur_suggestions[_sugg_selected_ix]);
	_sugg_selected_ix=-1;
	_cur_suggestions=null;
	_old_completable=null;
	_move_cursor_to_end(window,_cur_input_object,_cur_input_object.value.length);
}

function _trace_exception(exc,msg) {
	var msg_fmt="Javascript exception: "+(msg?msg:"")+" "+exc;
	if (_is_IE()) {
		msg_fmt+=" "+exc.name+": "+exc.message+" ("+exc.number+")"
	}
	var trace="";
	if (typeof exc=="string") {
		trace=exc+"\n"
	} else {
		for(var el in exc) {
			try {
				trace+=el+": "+exc[el]+"\n"
			} catch(e) {}
		}
	}
	trace+=_get_call_trace(_trace_exception.caller);
	_display_error(msg_fmt+"\n"+trace,1);
}

function _get_call_trace(fn) {
	try{
		if (!_is_IE()&&!(_is_user_agent_part("safari")||_is_user_agent_part("konqueror"))&&_is_user_agent_part("mozilla")) {
			return Error().stack;
		}
		if(!fn)return"";
		var trace="- "+_get_function_call_info(fn)+"(";
		for(var i=0;i<fn.arguments.length;i++) {
			if (i>0) trace+=", ";
			var args=String(fn.arguments[i]);
			if (args.length>40) {
				args=args.substr(0,40)+"...";
			}
			trace+=args;
		}
		trace+=")\n";
		trace+=_get_call_trace(fn.caller);
		return trace;
	} catch(e) {
		return"[Cannot get stack trace]: "+e+"\n";
	}
}
function _get_function_call_info(a) {
	var expr=/function (\w+)/;
	var b=expr.exec(String(a));
	if (b) {
		return b[1];
	}
	return"";
}

function _display_error_silent(msg) {
	_display_error(msg,0);
}

function _display_error(msg,show) {
	try {
		var msg_htm="["+((new Date).getTime())+"] "+_escape_special_chars(msg)+"<br/>";
		if (show==1) {
			if (_display_error_enabled) alert(msg_htm);
		}
	} catch(e){}
}


var _height_obj={
	ie:function(obj){
		return obj.document.body.clientHeight;
	},
	css:function(obj){
		return obj.document.documentElement.clientHeight;
	},
	def:function(obj){
		return obj.innerHeight;
	}
};

var _width_obj={
	ie:function(obj){
		return obj.document.body.clientWidth;
	},
	css:function(obj){
		return obj.document.documentElement.clientWidth;
	},
	def:function(obj){
		return obj.innerWidth;
	}
};

var _offs_left_obj={
	ie: function(obj) {
		return obj.document.body.scrollLeft;
	},
	css: function(obj) {
		return obj.document.documentElement.scrollLeft;
	},
	def:function(obj) {
		return obj.pageXOffset;
	}
};

var _offs_top_obj={
	ie: function(obj) {
		return obj.document.body.scrollTop;
	},
	css: function(obj) {
		return obj.document.documentElement.scrollTop;
	},
	def: function(obj) {
		return obj.pageYOffset
	}
};

function _get_window_metric(wnd,obj) {
	try {
		if (!window.opera&&"compatMode"in wnd.document&&wnd.document.compatMode=="CSS1Compat") {
			return obj.css(wnd);
		} else if (_is_IE()) {
			return obj.ie(wnd);
		}
	} catch(e) {}
	return obj.def(wnd);
}

function _get_frame_box(obj) {
	function get_offset(k) {
		for (var parent_offs=obj.offsetParent;parent_offs&&parent_offs.offsetParent;parent_offs=parent_offs.offsetParent) {
			if (parent_offs.scrollLeft) {
				k.x-=parent_offs.scrollLeft;
			}
			if (parent_offs.scrollTop) {
				k.y-=parent_offs.scrollTop;
			}
		}
	}
	var wnd;
	if (obj.ownerDocument&&obj.ownerDocument.parentWindow) {
		wnd=obj.ownerDocument.parentWindow;
	} else {
		wnd=window;
	}
	if (obj.ownerDocument&&obj.ownerDocument.getBoxObjectFor) {
		var obj_box=obj.ownerDocument.getBoxObjectFor(obj);
		var frame=new _frame_box(obj_box.x,obj_box.y,obj_box.width,obj_box.height,wnd);
		get_offset(frame);
		return frame;
	}
	if(obj.getBoundingClientRect) {
		var f=obj.getBoundingClientRect();
		return new _frame_box(f.left+_get_window_metric(wnd,_offs_left_obj),f.top+_get_window_metric(wnd,_offs_top_obj),f.right-f.left,f.bottom-f.top,wnd);
	}
	var x=0,y=0;
	for(var i=obj;i.offsetParent;i=i.offsetParent) {
		x+=i.offsetLeft;y+=i.offsetTop;
	}
	var frame=new _frame_box(x,y,obj.offsetWidth,obj.offsetHeight,wnd);
	get_offset(frame);
	return frame;
};

function _frame_box(x,y,w,h,parent) {
	this.x=x;
	this.y=y;
	this.w=w;
	this.h=h;
	this.coordinateFrame=parent||null;
}
_frame_box.prototype.contains=function(a) {
	return this.x<=a.x&&a.x<this.x+this.w&&this.y<=a.y&&a.y<this.y+this.h;
};
_frame_box.prototype.toString=function() {
	return"[R "+this.w+"x"+this.h+"+"+this.x+"+"+this.y+"]";
};


function _ac_select(selected_ix) {
	_sugg_selected_ix=selected_ix;
	_complete_input();
	if (_cur_storage.oncomplete) {
		_cur_storage.oncomplete(true,null,_cur_input_object,_cur_input_object.value);
	}
	_get_suggestions(null);
	_auto_complete_show(true,_cur_storage.f);
}

function _ac_isCompleteListShowing() {
	var obj=document.getElementById("autocompleteBox");
	return!(!_cur_storage)&&!_suggestion_disabled&&_cur_suggestions&&_cur_suggestions.length&&obj&&obj.innerHTML;
}

function _escape_special_chars(str) {
	return str.replace(/&/g,"&amp;").
		replace(/</g,"&lt;").
		replace(/\"/g,"&quot;").
		replace(/ /g,"&nbsp;").
		replace(/\r\n?|\n/g,"<br/>");
}