function QuickSearchTemplate(name, category) {
	var container = document.createElement('div');
	var nameDiv = document.createElement('div');
	name = name.replace('\\', '');
	if (category != null)
		var categoryDiv = document.createElement('div');

	nameDiv.innerHTML = name;
	if (category != null) {
		categoryDiv.className = 'category';
		categoryDiv.innerHTML = category;
	}
	
	container.appendChild(nameDiv);
	if (category != null)
		container.appendChild(categoryDiv);
	
	return container;
}

function QuickSearch(container, defaultText, addFunction) {
	this.init(container, defaultText, addFunction);
}

QuickSearch.prototype = {
	// default values
	defaultText:null,
	limit:false,
	pageScroll: 10,
	dropDownHeight: 300,
	
	// dom objects
	dropDown:null,
	inputElt:null,
	descriptionElt:null,
	optionsElt:null,
	
	// option attributes
	options:null,
	optionsDisplayed:null,
	optionsDisplayedCount:null,
	optionSelected:null,
	optionToAdd:null,
	
	// functions
	addFunction:null,
	approveFunction:null,
	clearFunction:null,
	
	// pagination
	currSearchIndex: null,
	
	
	// search
	timer: null,

	init: function (container, defaultText, addFunction) {
		try {
			if (typeof container == 'string')
				var container = document.getElementById(container);
			var dropDown = document.createElement('div');
			var inputElt = document.createElement('input');
			var statusDiv = document.createElement('div');
			var status = document.createElement('a');
			var optionsElt = document.createElement('div');
			var descriptionElt = document.createElement('div');
			var clearBoth = document.createElement('div');
			var self = this;
			
			inputElt.type = 'text';
			if (typeof(defaultText) != 'undefined')
				inputElt.value = defaultText;
			this.addClassName(inputElt, 'default');
			inputElt.setAttribute("autocomplete","off");
			inputElt.onkeypress = function(event) { 
				if(getKeyCode(event) == 13 || getKeyCode(event) == 38)
					return false; 
			};
			inputElt.onkeyup = function(event) {
				var key = getKeyCode(event);
				if (self.timer)
					clearTimeout(self.timer);
				self.timer = setTimeout(function() {
					self.onKeyUp(key);
					self.timer = 0;
				}, 100);
			};
			inputElt.onclick = function() {
				self.focus();
			};
			inputElt.onfocus = function(event) {
				self.focus();
			};
			inputElt.onblur = function(event) {
				self.blur();
			};
			
			status.href = '#';
			status.innerHTML = 'x';
			status.onclick = function(event){
				self.statusClick();
			};
			status.className = 'search';
			
			statusDiv.className = 'close';
			statusDiv.appendChild(status);
			
			clearBoth.style.clear = 'both';
			clearBoth.style.fontSize = '1px';
			
			optionsElt.style.display = 'none';
			optionsElt.className = 'options';
			optionsElt.onclick = function(event){
				self.approveOption(self.findOptionContainer(event, this));
				self.noBubble(event);
			};
			
			dropDown.className = 'dropDown';
			dropDown.style.display = 'none';
			dropDown.onclick = function(event) {
				self.approveOption(self.findOptionContainer(event, this));
				self.noBubble(event)
			};
			dropDown.appendChild(optionsElt);
			
			this.addClassName(container, 'quickSearch');
			container.appendChild(inputElt);
			container.appendChild(statusDiv);
			container.appendChild(clearBoth);
			container.appendChild(dropDown);
			
			this.inputElt = inputElt;
			
			this.dropDown = dropDown;
			this.descriptionElt = descriptionElt;
			this.optionsElt = optionsElt;
			this.statusLink = status;
			
			this.defaultText = inputElt.value;
			this.options = [];
			this.optionsDisplayed = {};
			this.optionsDisplayedCount = 0;
			
			this.timer = 0;
			
			if (addFunction) {
				this.addFunction = addFunction;
				this.optionToAdd = new QuickSearchOption(0, '');
				this.optionToAdd.addOption = true;
				dropDown.appendChild(this.optionToAdd.getContent());
				this.addSentence = 'Add ';
			}
			
			descriptionElt.className = 'description';
			dropDown.appendChild(descriptionElt);
		}
		catch (e) { alert("QuickSearch.init error: " + e.message); }
	},
	
	statusToggle: function() {
		if (this.dropDownVisible()) {
			this.statusLink.className = 'close';
		}
		else {
			this.statusLink.className = 'search';
		}
	},
	
	statusClick: function(focus) {
		if (this.statusLink.className == 'search') {
			this.statusLink.className = 'close';
			if (!focus)
				this.inputElt.focus();
		}
		else {
			this.statusLink.className = 'search';
			this.inputElt.value = '';
			this.blur();
			this.hideDropDown();
		}
		this.statusToggle();
	},
	
	focus: function() {
		if (this.inputElt.value == this.defaultText) {
			this.inputElt.value = '';
			this.removeClassName(this.inputElt, 'default');
			this.clearSearch();
		}
		if (this.hasDescription) {
			this.showDescription();
			this.showDropDown();
		}
	},

	blur: function() {
		if (this.inputElt.value == "") {
			this.addClassName(this.inputElt, 'default');
			this.inputElt.value = this.defaultText;
		}
	},
	
	onKeyUp: function(key) {
		switch (key) {
			case 13: // enter key
				this.approveOption(this.optionSelected);
				break;
			case 38: //key up
				this.selectPrevious();
				break;
			case 40: //key down
				this.selectNext();
				break;
			case 33: //page up
				this.selectPrevious(true);
				break;
			case 34: //page down
				this.selectNext(true);
				break;
			case 35: //key end
				this.selectLast();
				break;
			case 36: //key home
				this.selectFirst();
				break;
			case 37: //key left
				return false;
				break;
			case 39: //key right
				return false;
				break;
			case 27: //key escape
				this.hideDropDown();
				this.inputElt.value = '';
				this.inputElt.blur();
				break;
			default: //search
				this.clearSearch();
				if (this.inputElt.value == '') {
					this.hideDropDown();
				}
				else {
					if (this.addFunction)
						this.updateAddOption();
					
					this.search(this.inputElt.value);
					
					this.showDropDown();
					this.formatOptionsElt();
					if (!this.hasDescription)
						this.hideDescription();
					this.selectFirst();
				}
				break;
		}
	},
	
	selectOption: function(option) {
		if (!option)
			return;
		if (this.optionSelected)
			this.optionSelected.deselect();
		this.optionSelected = option;
		this.optionSelected.select();
	},
	
	selectFirst: function() {
		if (this.hasOptionsDisplayed || this.optionToAdd) {
			this.selectOption(this.optionsDisplayed[0]);
			this.scroll();
		}
	},
	
	selectLast: function() {
		if (this.hasOptionsDisplayed || this.optionToAdd) {
			this.selectOption(this.optionsDisplayed[this.optionsDisplayedCount - 1]);
			this.scroll();
		}
	},
	
	selectNext: function(pageScroll) {
		if (this.optionSelected) {
			if (pageScroll)
				var step = this.pageScroll;
			else var step = 1;
			if (this.optionsDisplayed[parseInt(this.optionSelected.displayId) + step]) {
				this.selectOption(this.optionsDisplayed[parseInt(this.optionSelected.displayId) + step]);
				this.scroll();
			}
			else if (pageScroll)
				this.selectLast();
		}
	},
	
	selectPrevious: function(pageScroll) {
		if (this.optionSelected) {
			if (pageScroll)
				var step = this.pageScroll;
			else var step = 1;
			if (this.optionsDisplayed[this.optionSelected.displayId - step]) {
				this.selectOption(this.optionsDisplayed[this.optionSelected.displayId - step]);
				this.scroll();
			}
			else if (pageScroll)
				this.selectFirst();
		}
	},
	
	scroll: function() {
		var offset = this.optionSelected.getContent().offsetTop;
		var offsetHeight = this.optionSelected.getContent().offsetHeight;
		if (offset < this.optionsElt.scrollTop)
			this.optionsElt.scrollTop = offset - 1;
		else if (offset > this.optionsElt.clientHeight + this.optionsElt.scrollTop - offsetHeight)
			this.optionsElt.scrollTop = offset + offsetHeight - this.optionsElt.clientHeight + 1;
	},
	
	updateAddOption: function() {
		this.optionToAdd.name = this.inputElt.value;
		this.optionToAdd.setContent(this.addSentence + this.inputElt.value);
	},
	
	setAddSentence: function(sentence) {
		this.addSentence = sentence;
	},
	
	setSelection: function(id) {
		if (!id) return;
		var option = this.searchOptionsById(id);
		if (option != false)
			this.approveOption(option);
		else return false;
	},
	
	setSelectionById: function(id) {
		this.setSelection(id);
	},
	
	setSelectionByName: function(name) {
		this.inputElt.value = name;
		this.approveOption();
	},
	
	setDescription: function(description) {
		this.descriptionElt.innerHTML = description;
		this.hasDescription = true;
	},
	
	setApproveFunction: function(name) {
		this.approveFunction = name;
	},
	
	setClearFunction: function(name) {
		this.clearFunction = name;
	},
	
	showAll: function() {
		this.displayOptions(this.options);
	},
	
	displayOptions: function(options) {
		var count = 0;
		this.optionsDisplayed = options;
		
		for (var i in options) {
			this.optionsDisplayed[i].displayId = i;
			options[i].show();
			count++;
		}
		
		if (count > 0)
			this.hasOptionsDisplayed = true;
		else this.hasOptionsDisplayed = false;

		if (this.optionToAdd && this.inputValue != '') {
			this.optionsDisplayed[count] = this.optionToAdd;
			this.optionsDisplayed[count].displayId = count;
			if (this.inputElt.value != '')
				this.optionToAdd.show();
			count++;
		}
		
		this.optionsDisplayedCount = count;
	},
	
	search: function(str, more) {
		this.displayOptions(this.searchOptionsByName(str, more));
	},
	
	searchOptionsByName: function(str, more) {
		var i = 0;
		if (more) {
			var j = this.currSearchIndex;
			var returnArray = this.optionsDisplayed;
		}
		else {
			var j = 0;
			var returnArray = {};
		}

		for (j; j < this.options.length; j++) {
			if (search(str, this.options[j].name)) {
				returnArray[i] = this.options[j];
				i++;
				if (this.limit && i >= this.limit) {
					this.currSearchIndex = j;
					return returnArray;
				}
			}
		}
		return returnArray;
	},
	
	searchOptionsById: function(id) {
		for (var i = 0; i < this.options.length; i++) {
			if (this.options[i].getId() == id) {
				return this.options[i];
			}
		}
		return false;
	},
	
	clearSearch: function() {
		if (this.clearFunction) {
			this.clearFunction();
		}
		for (var i in this.optionsDisplayed) {
			this.optionsDisplayed[i].reset();
		}
		this.optionsDisplayed = null;
		this.optionsDisplayedCount = 0;
		this.optionSelected = null;
		this.hideOptions();
	},
	
	formatOptionsElt: function() {
		this.showOptions();
		var options = this.optionsDisplayed ;
		var offsetHeight = 0;
		for (var i in options) {
			offsetHeight += options[i].getContent().offsetHeight;
			if (offsetHeight > this.dropDownHeight)
				break;
		}
		
		if (offsetHeight > this.dropDownHeight) {
			this.optionsElt.style.height = this.dropDownHeight + 'px';
			this.optionsElt.style.overflowY = 'scroll';
		}
		else {
			this.optionsElt.style.height = 'auto';
			this.optionsElt.style.overflowY = 'hidden';
		}
	},
	
	showDropDown: function() {
		if (this.optionToAdd || this.hasDescription || this.hasOptionsDisplayed)
			this.dropDown.style.display = 'block';
		this.statusToggle();
	},
	
	hideDropDown: function() {
		this.dropDown.style.display = 'none';
		this.statusToggle();
	},
	
	dropDownVisible: function() {
		if (this.dropDown.style.display == 'block')
			return true;
		else return false;
	},
	
	showDescription: function() {
		this.descriptionElt.style.display = 'block';
	},
	
	hideDescription: function() {
		this.descriptionElt.style.display = 'none';
	},
	
	descriptionVisible: function() {
		if (this.descriptionElt.style.display == 'block')
			return true;
		else return false;
	},
	
	showOptions: function() {
		this.optionsElt.style.display = 'block';
	},
	
	hideOptions: function() {
		this.optionsElt.style.display = 'none';
	},
	
	addOption: function(id, name, html, data) {
		name = name.replace('\\', '');
		var count = this.options.length;
		this.options[count] = new QuickSearchOption(id, name, html, data);
		this.optionsElt.appendChild(this.options[count].getContent());
		return this.options[count];
	},
	
	approveOption: function(option) {
		if (option) {
			if (option.addOption == true) {
				this.clearSearch();
				this.inputElt.value = '';
				this.inputElt.blur();
				this.hideDropDown();
				this.blur();
				this.addFunction(option.name);
				return;
			}
			if (this.approveFunction) {
				if (option.data) {
					this.approveFunction(option.id, option.name, option.data);
				}
				else this.approveFunction(option.id, option.name);
				
				if (!this.clearFunction) {
					this.clearSearch();
				}
				this.inputElt.value = '';
				this.inputElt.blur();
				this.hideDropDown();
				this.blur();
			}
		}
	},
	
	findOptionContainer: function(event, targetElt) {
		var target = this.getTarget(event);
		var prevTarget = target;
		while (target != targetElt) {
			prevTarget = target;
			if (!target.parentNode)
				return false;
			target = target.parentNode;
		}
		return prevTarget.self;
	},
	
	getTarget: function (e) {
		if (!e)
			e = window.event;
		if (e.target)
			return e.target;
		else if (e.srcElement)
			return e.srcElement;
		if (targ.nodeType == 3) // defeat Safari bug
			return targ.parentNode;
	},
	
	noBubble: function (e) {
		if (!e) 
			var e = window.event;
		e.cancelBubble = true;
		if (e.stopPropagation)
			e.stopPropagation();
	},
	
	addClassName: function(elt, className) {
		if (typeof elt == 'string')
			var elt = document.getElementById(elt);
		if (this.hasClassName(elt, className))
			return;
		else elt.className += ' ' + className;
	},

	removeClassName: function (elt, className) {
		if (typeof elt == 'string')
			var elt = document.getElementById(elt);
		if (this.hasClassName(elt, className)) {
			var classes = elt.className.split(' ');
			var tmp = '';
			for (var i = 0; i < classes.length; i++) {
				if (classes[i] != className) {
					tmp += classes[i];
					if (i + 1 < classes.length) tmp += ' ';
				}
			}
			elt.className = tmp;
		}
	},
	
	hasClassName: function(elt, className){
		if (elt.className.indexOf(className) > -1)
			return true;
		else return false;
	}
}

function QuickSearchOption(id, name, html, data) {
	this.init(id, name, html, data);
}
QuickSearchOption.prototype = {
	id:null,
	name:null,
	data:null,
	container:null,
	selected:null,
	hidden:null,
	displayId:null,
	addOption:null,

	init: function(id, name, html, data) {
		var container = document.createElement('div');
		var self = this;
		
		if (html == null)
			html = name;
		
		if (typeof(html) == 'string')
			container.innerHTML = html;
		else container.appendChild(html);

		container.onmouseover = function() { self.onMouseOver(); };
		container.onmouseout = function() { self.onMouseOut(); };
		container.self = this;

		this.id = id;
		this.data = data;
		this.name = name;
		this.container = container;
		this.addOption = false;
		this.reset();
	},
	
	reset: function() {
		this.deselect();
		this.hide();
		this.displayId = null;
	},
	
	getId: function() {
		return this.id;
	},
	
	getContent: function() {
		return this.container;
	},
	
	setContent: function(newContent) {
		if (typeof(newContent) == 'string')
			this.container.innerHTML = newContent;
		else {
			this.container.innerHTML = '';
			this.container.appendChild(newContent);
		}
	},

	select: function() {
		this.container.className = 'option selected';
		this.selected = true;
	},

	deselect: function() {
		this.container.className = 'option unselected';
		this.selected = false;
	},
	
	show: function() {
		this.container.style.display = 'block';
	},
	
	hide: function() {
		this.container.style.display = 'none';
	},
	
	onMouseOver: function() {
		if (this.selected == true)
			this.container.className = 'option selected hover';
		else this.container.className = 'option unselected hover';
	},

	onMouseOut: function() {
		if (this.selected == true)
			this.container.className = 'option selected';
		else this.container.className = 'option unselected';
	}
}

function search(search, haystack) {
	if (search == null || search == "")
		return false;

	var str = String(search).toLowerCase(); //cast to string incase javascript has 'cleverly' cast our name to a number
	var search_array = str.split(" ");
	var haystack = String(haystack).toLowerCase();

	for (var i = 0; i < search_array.length; i++)
		if ( haystack.indexOf(search_array[i]) == -1)
			return false; //Our work is done here.
	return true; //Only if it contained all of the filter words.
}