function FocusSearch(crit, critdiv, args) {
	this.crit = new Array();
	this.anchors = new Object();
	this.critFields = new Object();
	this.anchors.critdiv = critdiv;
	this.anchors.unitsdiv = null;
	this.criteria = "";
	this.resultsfield = "name";
	this.previewfield = "desc";
	if(args) {
		for(elem in args) {
			if(elem.indexOf("div") >= 0)
				this.anchors[elem] = args[elem];
			else
				this[elem] = args[elem];
		}
	}
	for(var i=0; i < crit.length; i++) {
		this.crit[this.crit.length] = crit[i];
		if(!crit[i].value)
			this.crit[i].value = "";
		this.crit[i].plugin = 
			new FocusSearch.pluginTypes[crit[i].fieldtype](this, i, {units: this.anchors.unitsdiv});
		this.critFields[crit[i].field] = this.crit[i];
	}
	if(this.initurivalue) {
		var inputArr = this.initurivalue.split("&");
		for(var i=0;i<inputArr.length;i++) {
			var temp = inputArr[i].split("=");
			if(this.critFields[decodeURIComponent(temp[0])])
				this.critFields[decodeURIComponent(temp[0])].value = decodeURIComponent(temp[1]);
		}		   
		delete inputArr;
		delete temp;
	}
}

FocusSearch.registerPlugin = function(name, pluginCtor) {
	if(!FocusSearch.pluginTypes)
		FocusSearch.pluginTypes = new Object();
	FocusSearch.pluginTypes[name] = pluginCtor;
}

FocusSearch.prototype.draw = function() {
	var drawTable = document.createElement("table");
	if($(this.anchors.critdiv).children("table").get().length)
		drawTable = $(this.anchors.critdiv).children("table").get(0);
	else
		$(this.anchors.critdiv).append(drawTable);
	$(drawTable).width( $(this.anchors.critdiv).width() );
	for(var i=0; i < this.crit.length; i++) {
		this.crit[i].plugin.draw(drawTable);
	}
	this.critUpdate();
}

FocusSearch.prototype.critUpdate = function(plugin) {
	this.criteria = "";
	if(this.targets)
		var results = this.targets.slice();
	if(this.critfunc && plugin)
		critfunc(plugin);
	for(var i=0; i < this.crit.length; i++) {
		if(this.crit[i].value) {
			this.criteria += this.crit[i].value + ",";
			for(var j=0; results && j < results.length; j++) {
				if(results[j]) {
					if(!this.crit[i].plugin.doesMatch(results[j])) {
						delete results[j];
	}	}	}	}	}
	if(results) {
		this.updatePlugins(results);
		if(this.groupfield)
			this.drawGroupResults(results);
		else
			this.drawRawResults(results);
	} else 
		this.updatePlugins();
	if(this.anchors.resultsdiv &&
	   !$(this.anchors.resultsdiv.mvc.tbody).children(".selected").filter(".subitem").get().length)
		this.showPreview();
}

FocusSearch.prototype.updatePlugins = function(in_data) {
	for(var i=0; i < this.crit.length; i++) {
		if(in_data)
			this.crit[i].plugin.update(in_data);
		else
			this.crit[i].plugin.update();
	}
}

//Draws results of search with grouping by specified category
FocusSearch.prototype.drawGroupResults = function(in_data) {
if(!this.targets || !this.anchors.resultsdiv)
		return;
	var engine = this;
	var categoryElems = new Object;
	//If there are open groups, inject the "subitems" back into the in_data and 
	// remember which groups were open.
	if($(engine.anchors.resultsdiv.mvc.tbody).children(".subitem").get().length) {
		$(engine.anchors.resultsdiv.mvc.tbody).children().not(
		  $(engine.anchors.resultsdiv.mvc.tbody).
		    children(".subitem").prev().not(".subitem")
		).not(".subitem").remove();
		var temp = $(engine.anchors.resultsdiv.mvc.tbody).children(".subitem").get();
		for(i in temp) {
			in_data[temp[i].selectid] = engine.targets[temp[i].selectid];
			if(temp[i].className.indexOf("selected") >= 0)
				var selectMe = temp[i].selectid;
		}
		var save = new Object();
		var temp = $(engine.anchors.resultsdiv.mvc.tbody).children(".groupitem").get();
		for(i in temp) {
			save[temp[i].selectid] = false;
			if(temp[i].className.indexOf("selected") >= 0)
				var selectMe = temp[i].selectid;
		}
		delete temp;
	}
	this.anchors.resultsdiv.empty();
	for(var i=0; i < in_data.length; i++) {
		if(!in_data[i])
			continue;
		if(in_data[i][this.groupfield] == "")
			in_data[i][this.groupfield] = "(No "+this.groupfield+")";
		//Store each item's unique id in an array according to the value in its target groupfield
		if(categoryElems[in_data[i][this.groupfield]]) {
			categoryElems[in_data[i][this.groupfield]].results += i + ",";
		} else {
			var label = [in_data[i][this.groupfield]];
			categoryElems[in_data[i][this.groupfield]] = 
				engine.anchors.resultsdiv.
					addRow(in_data[i][this.groupfield],
					label,
					{
					setClass: "groupitem",
					onclick: function(id) {
						var groupChildren = new Array();
						var groupEl = $(engine.anchors.resultsdiv.mvc.tbody).
							children(".groupitem").filter(".selected").get(0);
						for(groupEl; groupEl.nextSibling &&
							  groupEl.nextSibling.className.indexOf("subitem") >= 0;
							  groupEl = groupEl.nextSibling)
							groupChildren[groupChildren.length] = groupEl.nextSibling;
						if(groupChildren.length) {
							$(groupChildren).remove();
							return false;
						}
						var subitems = categoryElems[id].results.split(",");
						var reordered = new Array();
						reordered.length = subitems.length;
						$.each(subitems, function(i, elem) {reordered[(reordered.length-1)-i] = elem;});
						$.each(reordered, 
							function(i,elem) {
								if(in_data[elem] && in_data[elem][engine.resultsfield]) {
									engine.anchors.resultsdiv.addRow(
										elem,
										[in_data[elem][engine.resultsfield]],
										{
										 setClass: "subitem",
										 after: categoryElems[id],
										 onclick: function(id) {
											if(engine.pickfunc)
												engine.pickfunc(in_data[id]);
											if(engine.anchors.previewdiv)
												engine.showPreview(in_data[id]);
											}
										}
									);
								}
							}
						);
					}}
				);
			categoryElems[in_data[i][this.groupfield]].results = i + ",";
		}
	}
	for(groupitemRow in save) {
		if(save[groupitemRow]) {
			var selectMe = categoryElems[groupitemRow];
			continue;
		}
		categoryElems[groupitemRow].onclick();
	}
	if(selectMe) {
		$.each($(engine.anchors.resultsdiv.mvc.tbody).children().get(), 
		  function(i, row) {
			if(row.selectid == selectMe) {
				if(row.className.indexOf("groupitem") < 0)
					row.onclick();
				else {
					engine.anchors.resultsdiv.mvc.selectRow(row);
				}
			}			
		});
		delete selectMe;
	} else
		$(engine.anchors.resultsdiv.mvc.tbody).children().removeClass("selected");
	delete engine;
}

//Draws results of search individually
FocusSearch.prototype.drawRawResults = function(in_data) {
	if(!this.targets || !this.anchors.resultsdiv)
		return;
	var engine = this;
	engine.anchors.resultsdiv.empty();
	for(var i=0; i < in_data.length; i++) {
		if(!in_data[i])
			continue;
		engine.anchors.resultsdiv.
		addRow(i,
				[in_data[i][engine.resultsfield].toString()],
				{onclick: function(id) {
					if(engine.pickfunc)
						engine.pickfunc(in_data[id]);
					if(engine.anchors.previewdiv)
						engine.showPreview(in_data[id]);
					}});
	}
	delete engine;
}

FocusSearch.prototype.showPreview = function(dataobj) {
	if(!this.anchors.previewdiv)
		return;
	if(!dataobj) {
		$(this.anchors.previewdiv).html("");
		return;	
	}
	$(this.anchors.previewdiv).html(dataobj[this.previewfield]);
} 

FocusSearch.prototype.cleanup = function() {
	for(var i=0; i < this.targets.length; i++) {
		for(elem in this.targets[i]) {
			if(elem.indexOf("_fs") >= 0)
				delete this.targets[i][elem];
		}
	}
}

FocusSearch.prototype.getSelected = function() {
	if(!this.anchors.resultsdiv || !this.targets)
		return;
	var engine = this;
	if(this.idfield)
		return $.map(
			$(this.anchors.resultsdiv.mvc.tbody).children(".selected").get(), 
				function(n) {
					return engine.targets[n.selectid][engine.idfield];
				}
			);
	delete engine;
}

function objectIsEmpty(obj) {
	for(var i in obj) 
		return false;
	return true;
}
