(function($){
	
	RegExp.escape = function(str) {
		var specials = new RegExp("[.*+?|()\\[\\]{}\\\\]", "g"); // .*+?|()[]{}\
		return str.replace(specials, "\\$&");
	}
	
	$.fn.setCursorPosition = function(pos) {
		if ($(this).get(0).setSelectionRange) {
			$(this).get(0).setSelectionRange(pos, pos);
		} else if ($(this).get(0).createTextRange) {
			var range = $(this).get(0).createTextRange();
			range.collapse(true);
			range.moveEnd('character', pos);
			range.moveStart('character', pos);
			range.select();
		}
	}
	
	$.fn.scrollbarWidth = 	function() {
		var $p = $('<p style="width:100%;height:200px;"></p>');
		var $div = $('<div style="position:absolute;top:0px;left:0px;width:200px;height:150px;overflow:hidden;visibility:hidden"></div>');
		$div.append($p);
		$('body').append($div);
		var div = $div[0];
		var w1 = div.offsetWidth;
		$div.css({'overflow':'scroll'});
		var w2 = div.offsetWidth;
		if (w1==w2) w2 = div.clientWidth;
		return w1 - w2;
	}
	
	var concepts = { 'request':'sc' }
	
	var levels = { 'data': new Array(
			{'caption': "<em>Basic [Level]</em>",'value':'56578993'},
		{'caption': "<em>Intermediate [Level]</em>",'value':'56578994'},
		{'caption': "<em>Advance [Level]</em>",'value':'56578995'}
		)
	}

	var types = { 'data': new Array(
			{'caption': "<em>Article [Type]</em>",'value':'66578996'},
		{'caption': "<em>Discussion [Type]</em>",'value':'66578997'},
		{'caption': "<em>Link [Type]</em>",'value':'66578998'},
		{'caption': "<em>Question [Type]</em>",'value':'66578999'}
		)
	}
	
	var genres = {'data': new Array(
			{'caption': "<em>FAQ [Genre]</em>:listed questions and answers, all supposed to be frequently asked in some context",'value':'86579351'},
		{'caption': "<em>How-to [Genre]</em>:step by step instructions of how to accomplish some specific task",'value':'86579352'},
		{'caption': "<em>Tutorial [Genre]</em>:guides to learn a topic",'value':'86579353'},
		{'caption': "<em>News [Genre]</em>:information on current events",'value':'86579354'},
		{'caption': "<em>Benchmark [Genre]</em>:result of assessment on relative performance of objects by running a number of standard tests",'value':'86579355'},
		{'caption': "<em>Academic Paper [Genre]</em>:scholarly introduction and presentation for scrutiny of new research, and the critique of existing research.",'value':'86579356'},
		{'caption': "<em>Factsheet [Genre]</em>:a presentation of facts in a format emphasizing brevity, key points of interest or concern",'value':'86579357'},
		{'caption': "<em>Gallery [Genre]</em>:A collection of images",'value':'86579358'},
		{'caption': "<em>Commentary [Genre]</em>:your personal point of view",'value':'86579359'},
		{'caption': "<em>Report [Genre]</em>:specific, focused content - often the result of an experiment, investigation, or inquiry to a specific audience",'value':'86579360'},
		{'caption': "<em>Comparison [Genre]</em>:deals with similar topics where differences are clearly displayed and examined",'value':'86579361'},
		{'caption': "<em>Fiction [Genre]</em>:invented or imaginative writing, instead of real facts",'value':'86579362'},
		{'caption': "<em>Review [Genre]</em>:gives evaluation of a book, film, music, play, etc",'value':'86579363'}
		)
	}

	var langs = {'data': new Array(
			{'caption': "<em>Chinese [Language]</em>:中文(繁/簡)",'value':'96579364'},
		{'caption': "<em>English [Language]</em>:English",'value':'96579365'},
		{'caption': "<em>French [Language]</em>:Français",'value':'96579366'},
		{'caption': "<em>German [Language]</em>:Deutsch",'value':'96579367'},
		{'caption': "<em>Italian [Language]</em>:Italiano",'value':'96579368'},
		{'caption': "<em>Japanese [Language]</em>:日本語",'value':'96579369'},
		{'caption': "<em>Korean [Language]</em>:한국어",'value':'96579370'},
		{'caption': "<em>Portuguese [Language]</em>:Português",'value':'96579371'},
		{'caption': "<em>Russian [Language]</em>:Русский",'value':'96579372'},
		{'caption': "<em>Spanish [Language]</em>:Español",'value':'96579373'}
		)
	}

	var allentries=new Array(concepts,0,0,0,levels,types,0,genres,langs);
	
	var all = { 'request':'sc',			//search concept
			'identity':true,
			'data':new Array(0)
	}
	
	var typeName = new Array('Concept','Wikicategory','Redirection','Disambiguous','Level','Type','Category','Genre','Language');
	
	var allthings = { 'level':levels,
			'genre':genres,
			'type':types,
			'concept':concepts,
			'language':langs,
			'all':all
	}
	
	var nullfunction = function(){return 0};
	
	$.fn.extend({
		searchbar: function(options) {
			options = $.extend({
				'ajax':'',
				'height':'10',
				'query': 'q',
				'allthings':allthings,
				'tagMode':0,
				'tagControl':null,
				'graymsg':'',
				'activation':null,
				'deactivate':null,
			}, options);
			return new $.Searchbar(this, options);
		}
	});



	$.Searchbar = function($inputdiv, options) {
		var sbar = this;
		var initdone=false;
		var addHiddenInput = function(value){
			var input=document.createElement('input');
			$(input).attr({'type':'hidden','name': options.query ,'value':value});
			return input;
		}
		
		var KEY = {
				UP: 38,
				DOWN: 40,
				BACKSPACE: 8,
				TAB: 9,
				ENTER: 13,
				ESC: 27,
				COMMA: 188,
				PAGEUP: 33,
				PAGEDOWN: 34
		}
		
		this.addItem = function(item) {					// item = { 'value':123, 'name': 'hello'}
			if ($holder.find('.bit-box[val='+item.value+']').length>0)
				return false;
			var $li = $('<li class="bit-box"></li>').attr({'val':item.value});
			if(item.value.charAt(0)>=5){
				$li.attr({tagType:'attribute'}); //in IE, type cannot be changed through jquery
			}
			$li.text(item.name);
			var $a = $('<a class="closebutton" href="#"></a>');
			var input = addHiddenInput(item.value);
			$li.append($a).append(input);
			$holder.append($li);
			$clear.show();
			$a.click(function(){
				$(this).parent('li').fadeOut('fast',function(){
					if($(this).parents('.holder').children('li').size()==1){
						$clear.hide();
					}
					$(this).remove();

				});
				return false;
			});
		}
		
		this.checkControl = function(){
			var typeCount = new Array(options.tagControl[0].length);	
			for(var i=0;i<typeCount.length;i++)
				typeCount[i]=0;
			$holder.find('.bit-box').each(function(){
				typeCount[parseInt($(this).attr("val").charAt(0))-1]++;
			})
			for(var i=0;i<typeCount.length;i++){
				if(options.tagControl[1][i]>typeCount[i]){
					$("<div title='Tag Selection Error'><p>You have to select at least "+options.tagControl[1][i]+" "+ typeName[i]+" tags</p></div>").dialog();
					return false;
				}
			}
			return true;
		}
		
		var errormsg = function(target, msg){
			var s = 'val=' + target.attr('val');
			if ($searchbar.find('.ui-state-error['+s+']').length > 0) return;
			var errordiv=$("<div class='ui-state-error ui-corner-all' style='position:absolute;padding:0.7em 0.7em;z-index:11' "+s+"><span class='ui-icon ui-icon-alert' style='float: left; margin-right: 0.3em;'/>"+msg+"</div>");
			$searchbar.append(errordiv);
			var left=$menucontainer.offset().left-errordiv.outerWidth();
			if(left<0){
				left=$menucontainer.offset().left+$menucontainer.outerWidth();
			}
			errordiv.css({left:left,top:target.offset().top});
			$input.focus();
		}
		
		var getInternalItems = function(items ,target) {
			var reg = new RegExp(RegExp.escape(target) + ".*</em>",'i');
			results = [];
			$.each(items, function(i, item) {
				if (item.caption.match(reg)) results = results.concat(item);
			});
			return results;
		}
		
		var showDisambiguation = function(data) {
			if (oldmenu) {
				oldmenu.$menu.remove();
			}
			menu.$feed.children(".wait").removeClass('wait');
			dmenu = new DropMenu(1);
			oldmenu = menu;
			menu = dmenu;
			var h1 = $menucontainer.height();
			var w1 = oldmenu.$menu.width();
			menu.addFeedItem(data);
			$discontainer.append(menu.$menu);
			$menucontainer.height(h1);
			menu.$feed.show();
			menu.$msg.show();
			$menucontainer[0].scrollTop = 0;
			animate = true;
			$discontainer.animate({left:-w1+'px'},500,function(){
				$discontainer.css({'left':''});
				oldmenu.$menu.hide();
				menu.bindEvents();
				state.dis=null;
				$input.setCursorPosition(state.text.length);
				animate = false;
			});
		}
		
		var disambiguationBack = function() {
			var w1 = oldmenu.$menu.width();
			menu.$left_back.remove();
			$discontainer.css({'left':-w1+'px'});
			oldmenu.$menu.show();
			$menucontainer[0].scrollTop = 0;
			animate = true;
			$discontainer.animate({left: '0px'},500,function(){
				menu.$menu.remove();
				menu= oldmenu;
				oldmenu = null;
				$discontainer.css({'left':''});
				menu.bindEvents();
				$input.setCursorPosition(state.text.length);
				animate = false;
				b_on = false;
			});
		}
		
		var addServerItems = function(request, target, page) {
			if (!page) page=1;
			if (state.timer) clearTimeout(state.timer);
			if (cache[[request, target]] && page==1) {
				var add_data = cache[[request, target]]['data'];
				if (cache[[request, target]]['len'] > cache[[request, target]]['page']*20)
					add_data = add_data.concat({'value':'more',
							'caption':'Click or Press Enter for more items ('+ (cache[[request, target]]['len'] - page*20) +' more items)',
							'attr': {'request':request, 'target':target, 'page': cache[[request, target]]['page'] + 1}
					});
				if (request=='d') {
					showDisambiguation(add_data);
				} else {
					menu.$feed.children('.addPage').remove();
					menu.addFeedItem(add_data);
				}
				state.setState();
			} else {
				state.timer = setTimeout(function() {
					var hash = {};
					hash[request]=target;
					hash['p']=page;
					$.getJSON(options.ajax, hash, function(data) {
						if (request =='d' && page==1) {
							if ((request==state.request) && (target==state.dis)) {
								if (data.len > 20)
									showDisambiguation(data.data.concat({'value':'more', 
										'caption':'Click or Press Enter for more items ('+ (data.len - page*20) +' more items)',
										'attr': {'request':'d', 'target':target, 'page': page + 1}
									}));						
								else
									showDisambiguation(data.data);
								state.setState();
							}
						} else if ((request==state.request && target == state.newtext) || (request=='d' && state.request=='d' && target==state.dis)) {
							menu.$feed.children('.addPage').remove();
							if (data.len > page * 20)
								menu.addFeedItem(data.data.concat({'value':'more', 
									'caption':'Click or Press Enter for more items ('+ (data.len - page*20) +' more items)',
									'attr': {'request':request, 'target':target, 'page': page + 1}
								}));						
							else
								menu.addFeedItem(data.data);
							state.setState();
							menu.tohighlight = page * 20;
						}
						if (cache[[request, target]]) {
							cache[[request, target]]['data'] = cache[[request, target]]['data'].concat(data.data);
							cache[[request, target]]['page']+=1;
						} else {
							cache[[request, target]] = {'data':data.data, 'len':data.len, 'page': 1};
						}
					});
				}, 200);
				
				if (request!='d') {
					state.wait(request, target);
				} else {
					state.dis = target;
					state.request = 'd';
				}
			}
		}
		
		var DropMenu = function(dis) {
			this.menutop = 0;
		
			var welcomeText=options.tagMode?"Please enter concept tags":"Please enter keyword or concepts";

			var mblur = function() {
				$menucontainer.unbind('blur', mblur);
				if (mc_on)
					$input.focus();
				else
					self.hide();
			}
			
			var menuout = function(){
				$menucontainer.unbind('mouseout', menuout);
				if (!state.inputfocus && !mc_focus)
					self.hide();
				else
					$menucontainer.blur(mblur);
			};
			
			var self = this;
			this.dis = dis;
			this.$msg =$('<div class="default"></div>').html(welcomeText);
			this.$feed = $('<ul id="feed" class="feed"></ul>');
			this.feed = this.$feed[0];
			this.$menu= $('<div class="drop-menu"></div>');
			this.menu_show = false;
			this.tohighlight = null;
			if (this.dis) {
				this.wback = 15; 
				this.$left_cell = $('<td class="left-cell"></td>').css({"width":this.wback + "px"});
				this.$left_back = $('<div class="back-button"></div>').width(this.wback).css({"top":menucontainer.offsetTop, "left":menucontainer.offsetLeft});
				$('body').append(this.$left_back);
				this.$right_menu = $("<td></td>").append(this.$msg).append(this.$feed).css({"width":(wmenu - this.wback) + 'px', "padding":"0"});
				this.$row=$('<tr></tr>').append(this.$left_cell).append(this.$right_menu);
				this.$table = $('<table cellspacing="0" cellpadding="0"></table>').append(this.$row);
				this.$menu.append(this.$table);
				this.$left_back.hover(
					function(){
						b_on = true;
						self.$left_back.addClass("hover");
					}, function (){
						b_on = false;
						self.$left_back.removeClass("hover");
					}	
				).click(disambiguationBack);
			} else {
				this.$menu.append(this.$msg).append(this.$feed);
			}

			this.removeDisambiguation = function() {
				self.dis = null;
				self.$table.remove();
				self.$menu.append(self.$msg).append(self.$feed);
				self.$left_back.remove();
			}
			
			this.show = function () {
				self.$feed.show();
				self.$menu.show();
				$menucontainer.show();
				$discontainer.show();
				options.activate();
				if (self.dis)
					self.$left_back.show();
				if (!self.menu_show) {
					menucontainer.scrollTop = this.menutop;
					self.menu_show = true;
				}
			}

			this.hide = function () {
				if (animate) {
					return;
				}
				self.menutop = menucontainer.scrollTop;
				self.menu_show = false;
				$menucontainer.hide();
				options.deactivate();
				if (self.dis)
					self.$left_back.hide();
				$searchbar.find('.ui-state-error').remove();
			}
			
			this.$autofocus = null;
			this.showMsg = function() {
				self.$msg.removeClass('wait');
				if (self.$feed.children('li').length==0) {
					if ($input.val()) {
						self.$msg.html('No matches found!');
					} else {
						self.$msg.html(welcomeText);
					}
				} else {
					self.$msg.html(welcomeText);
				}
			}
			
			this.autoscroll = function(){
				if (!self.$autofocus) return;
				if (menucontainer.scrollTop > self.$autofocus[0].offsetTop)
					menucontainer.scrollTop = self.$autofocus[0].offsetTop;
				if (self.$autofocus[0].offsetTop + self.$autofocus.height() + 11 > menucontainer.scrollTop + menucontainer.clientHeight)
					menucontainer.scrollTop = self.$autofocus[0].offsetTop + self.$autofocus.height() + 11 - menucontainer.clientHeight;	
			}
			
			this.highlight = function(focus) {
				if (self.$autofocus) self.$autofocus.removeClass('auto-focus');
				if (focus) {
					self.$autofocus = focus.addClass('auto-focus');
				} else {
					self.$autofocus = null;
				}
				$searchbar.find('.ui-state-error').remove();
			}
			
			this.keyUp = function() {
				if (!self.$autofocus) return;
				if (self.$autofocus.prev().length > 0) self.highlight(self.$autofocus.prev());
				self.autoscroll();
			}
			
			this.keyDown = function() {
				if (!self.$autofocus) return;
				if (self.$autofocus.next().length > 0) self.highlight(self.$autofocus.next());
				self.autoscroll();
			}

			this.clearFeed = function() {
				self.$feed.children('li').remove();
				self.highlight();
			}
			
			this.inputBlur = function(e) {
				if (mc_on) {
					$menucontainer.bind('mouseout', menuout);
				} else if (!b_on) {
					self.hide();
				}

			}
			
			this.itemSelected = function($item) {
				if (!$item) $item = self.$autofocus;
				if (!$item) return false;
				var val=$item.attr('val');
				var c = val.charAt(0);
				if ($holder.find('.bit-box[val='+val+']').length>0){
					errormsg($item,"You have already selected this tag");
					return false;
				}

				if (c=='m'){
					$item.addClass('wait');
					addServerItems($item.attr('request'), $item.attr('target'), parseInt($item.attr('page')));
				} else if (c=='4') {	//disambiguation
					self.$autofocus.addClass('wait');
					addServerItems('d', $item.attr('val').substr(1));
					return true;
				} else{
					//(c=='0' || c=='1' || c=='2' || c=='5' || c=='6' || c=='7') {				//concepts or levels or keywords
					
					if(options.tagMode){
						var Ic=parseInt(c)-1;
						var typeCount =0;
						$holder.find('.bit-box').each(function(){	//count the number of selected tag with tag type = c
							if($(this).attr("val").charAt(0)==c)
								typeCount++
						})
						if(options.tagControl[0][Ic]<typeCount+1){
							errormsg($item,"You can at most select "+options.tagControl[0][Ic]+" "+ typeName[Ic]+" tags");
							return false;
						}
					}
					var reg = new RegExp("<em>(.*)</em>","i");
					var c = $item.html().match(reg);
					if (c) {
						sbar.addItem({'name':c[1],'value':val});
					} else {
						sbar.addItem({'name':$item.text(),'value':val});
					}
					$input.val('');
					$input.focus();
					state.changed();
					return true;
				}
				return false;
			}
			
			this.bindEvents = function () {
				self.$feed.children('li').hover(
					function () {
						self.highlight($(this));
					}, 
					function () {}
				).unbind('click')
				.click(function() {
					self.itemSelected($(this));
				});

				self.show();
				if (self.$menu.height() > hmenu) {
					self.$menu.width(wmenu - wscroll);
					$menucontainer.height(hmenu);
					if (self.dis)
						self.$left_back.height(hmenu);
				} else {
					self.$menu.width(wmenu);
					$menucontainer.height(self.$menu.height());
					if (self.dis)
						self.$left_back.height(self.$menu.height());
				}
				
				self.feed.scrollTop = 0;
				if (self.tohighlight) {
					self.highlight($(self.$feed.children('li')[self.tohighlight]));
					self.tohighlight=null;
				} else 
					self.highlight(self.$feed.children('li:first'));
				if(initdone)
					$input.focus();
			}
			
			this.addFeedItem = function(data) {
				$.each(data, function(i, item) {
					var $li = $("<li>").attr({'val': item.value}).html(item.caption);
					if (item.value) {
						if (item.value.charAt(0)=="4") 
							$li.addClass("disambiguation");
						else if(item.value.charAt(0)=="m")
							$li.addClass("addPage");
						if (item.attr)
							$li.attr(item.attr);
					}
					self.$feed.append($li);           
				});
				self.bindEvents();
			}
			
			this.wait = function() {
				self.$msg.addClass('wait');
				self.$msg.html('Please wait');
			}
			
			this.updateFeed = function(status, etext) {
				self.clearFeed();
				self.menutop = 0;
				var current = allthings[status];
				var additems = []
				if (current!=null) {
					if (current['identity'] && etext && !options.tagMode) additems = additems.concat({'caption': 'Keyword: ' + etext, 'value':'0' + etext});
					if (current['data']) additems = additems.concat(getInternalItems(current.data, etext));
					self.addFeedItem(additems);
					if (current['request'] && etext) {
						addServerItems(current['request'], etext);
					} else {
						state.setState();
					}
				}	
			}	
		}
		
//		var $status_bar = $("<p></p>");
//		$('#logo').append($status_bar);
		var wmenu = 572;
		var wscroll = $().scrollbarWidth();
		var hmenu = 240;
		var cache = {};
		var allthings = options.allthings;
		var $input = $inputdiv.find(":text");
		$input.attr({'class':'maininput','id':'input'});
		var $searchbar = $('<div id="searchbar" class="searchbar"></div>');
		//$inputdiv.wrap($searchbar);
		$inputdiv.before($searchbar);
		$searchbar.append($inputdiv);
		var $menucontainer = $('<div class="menucontainer" style="display:none"></div>');
		var mc_focus = false;
		var mc_on = false;
		var b_on = false;
		var animate = false;
		var menucontainer = $menucontainer[0];
		var $discontainer = $('<div class="discontainer"></div>');
		$menucontainer.append($discontainer);
		var $inputfeed = $('<div class="inputfeed"></div>');
		var $holder=$('<ul class="holder"></ul>');
		var $clear=$("<i id='clearfeed' class='sp sp-action_delete' style='display:none' title='Clear All' />");
		$searchbar.append($holder);
		$inputfeed.append($inputdiv);
		$holder.after($clear);
		var menu = new DropMenu();
		$discontainer.append(menu.$menu);
		$inputfeed.append($menucontainer);
		$searchbar.append($inputfeed);
		var $searchtype = $("#search_type");
		var dmenu = null;
		var oldmenu = null;

		for(var i=0;i<allentries.length;i++)
			if(allentries[i]!=0 && ((options.tagMode && options.tagControl[0][i]>0) || !options.tagMode)){
				if('data' in allentries[i])
					all.data = all.data.concat(allentries[i].data);
				$searchtype.append($("<option value="+typeName[i].toLowerCase()+">"+typeName[i]+"</option>"))
			}
	
		var Status = function() {
			var self = this;
			this.status = $searchtype.val();
			this.text = $input.val();
			this.inputfocus = false;
			this.dis = null;
			this.timer = null;
			this.request = null;
			
			this.changed = function() {
				var newtext = $input.val();
				var newstatus = $searchtype.val();
				if (newtext!=self.text || newstatus !=self.status) {
					if (menu.dis) {
						menu.removeDisambiguation();
					}
					menu.updateFeed(newstatus, newtext);
					self.text = newtext;
					self.status = newstatus;
				}
			}
			
			this.setState = function() {
				self.timer = null;
				self.request = null;
				self.target = null;
				menu.showMsg();
			}
			
			this.wait = function(request, newtext) {
				self.newtext = newtext;
				self.dis = null;
				self.request = request;
				menu.wait();
			}
		}

		var state = new Status();
		menu.updateFeed(state.status, state.text);
		$searchtype.change(function(){
			state.changed();
		});

		$input.focus(function(){
			state.inputfocus=true;
			$menucontainer.css({'left':$input.position().left});
			menu.show();
			if($input.val()==options.graymsg)
				$input.css({'color':'#000','font-style':'normal'}).val('');
		}).blur(function(){
			state.inputfocus=false;
			menu.inputBlur();
		}).keydown(function(event){			// IE only generates keydown when backspace is pressed
			switch(event.keyCode){
			case KEY.ENTER:
				menu.itemSelected();
				return false;
			case KEY.BACKSPACE:
				if (!$input.val()) {
					if ($('#bit-box-delete').length==0) {
						var bitbox=$holder.children().filter('.bit-box');
						$(bitbox[bitbox.length-1]).attr({'id':'bit-box-delete'}).addClass('bit-box-delete');
					} else $('#bit-box-delete a').click();
				}
				break;
			case KEY.UP:
				menu.keyUp();
				break;
			case KEY.DOWN:
				menu.keyDown();
				break;
			default:
				$('#bit-box-delete').attr({'id':''}).removeClass('bit-box-delete');
			}		
		}).keyup(function(event) {					//not using change since typing will not generate change unless the input is blurred
			if (event.keyCode==KEY.ENTER) return false;
			state.changed();
		}).blur();
		
		$menucontainer.focus(function() {
			mc_focus = true;
		}).blur(function() {
			mc_focus = false;
		}).bind('mouseover', function() {
			mc_on = true;
		}).bind('mouseout', function() {
			mc_on = false;
		});
		
		$clear.click(function(){
			$holder.find('.bit-box a').click();
			$(this).hide();
		});
		
		$(window).resize(function(){
			$menucontainer.css({'left':$input.position().left});
			if (menu.$left_back) {
				menu.$left_back.css({"top":menucontainer.offsetTop, "left":menucontainer.offsetLeft});
			}
		});
		initdone=true;
	};
})(jQuery)
