jQuery.fn.subjectTags = function(callerSettings) {
	settings = jQuery.extend({
    subjectsList: null,
    subjectsExisting: null,
    thisClass: null,
    ajaxEnabled: null,
		helptext_initial: 'Type in a subject name to search, up/down to select, enter to add',
		helptext_add: 'Are you sure you want to add a new subject?'
	}, callerSettings || {});
	
	// 	Create/Locate Required Elements	
	inputfield = jQuery(this).children('.subject');
	subjectlist = jQuery(this).children('.subject_list');
	submitbutton = jQuery(this).children('button[type="submit"]')
	addbutton = jQuery(this).children("button.add");
	if (jQuery('#' + settings.thisClass + '\\[subject_names\\]').length == 0) addbutton.after('<input id="' + settings.thisClass + '[subject_names]" class="submitted-subjects" value="" type="hidden" name="' + settings.thisClass + '[subject_names]"/>');
	hiddenbox = jQuery('#' + settings.thisClass + '\\[subject_names\\]');
	inputfield.after('<div class="subject_filter"><p>'+settings.helptext_initial+'</p><ul></ul></div>');
	subjectfilter_wrap = jQuery(this).children('.subject_filter');
	help = subjectfilter_wrap.children('p')
	subjectfilter = subjectfilter_wrap.children('ul')
	subjectfilter_wrap.hide();
	cursoron = false;
	focused = false;

	String.prototype.stripHTML=function() {
		return this.replace(/(<.+>\s*)/gi,"")
	}
	
	String.prototype.escapeRegExp = function () {
	    return this.replace(/([.*+?^${}()|[\]\/\\])/g, "\\$1");
	};

// 	Get Requested JSON

	var jsondata
	inputfield.after("<img class='spinner' src='/images/spinner16_grey.gif' style='margin-left: -23px; margin-right: 7px'/>");
	spinner = inputfield.siblings('img.spinner');
	inputfield.attr("disabled", true)
	jQuery.getJSON(settings.subjectsList,
		function(json){ 
			jsondata = json;
			inputfield.attr("disabled", false);
			spinner.hide();
		}
	);

	
//	 Bind Events To Elements
	
	inputfield.focus(function(e){
		focused = true;
		subjectfilter_wrap.show();
	}).blur(function(){
		focused = false;
		subjectfilter_wrap.fadeOut(100);
	}).keyup(filter).parents('form').submit(function(){
		hiddenbox.val(jsonify());
		if (focused) {
			return false;
		} else {
			if (settings.ajaxEnabled) {
				Hobo.ajaxRequest(this, [Hobo.partFor(this)]);
				return false;
			}
		}
	});
	
	addbutton.click(function(){
		if (!cursoron) addfiltered();
	});
	
	submitbutton.click
	
	jQuery('.results a').click(function(){
		jQuery('.results pre').empty().append(jsonify());
		return false;
	})
	
	subjectlist.children('li').children('a').click(function(){return removeSubjectHandler(this)});
	
	function jsonify(){
		var array = jQuery.makeArray(subjectli());
		if (inputfield.attr('value') != '') array.push(inputfield_cleaned());
		return array.toJSON();
	}
	
//	The main bit...

	function filter(e){
		// Filter Functions		
		termtext = jQuery.trim( jQuery(this).val().toLowerCase() );
		subjectfilter.empty();
		if ( !termtext ) {
			help.text(settings.helptext_initial).show();
		} else {
			help.hide();
		  	showmax = 7;
			found = false;
	  		jQuery.each(jsondata, function(index,item){
					if(showmax>0 && item.indexOf(termtext) != -1) {
						subjectfilter.append('<li class="filterlistitem"><a class="add_subject" href="#">'+highlight(item,termtext)+'</a></li>');
						showmax--;
						found = true;
					} 
	  		});
	  		subjectfilter.children('li').children('a').mousedown(function(e){
	  			inputfield.attr('value',jQuery(this).text())
	  			addfiltered();
	  		})
	  		subjectfilter.children('li').children('a').click(function(e){
	  			return false;
	  		})
	  		if (!found) help.text(settings.helptext_add).show();
		}
		
		// Keyboard Functions
		if ((e.keyCode >= 48 && e.keyCode <= 90) || e.keyCode == 8 || e.keyCode == 46) {
			currentindex = 0;
			cursoron = false;	
		} else if (e.keyCode == 38 || e.keyCode == 40) { // 38 up 40 down
			subjectfilterlist = subjectfilter.children('li');
			if (!cursoron) {
				currentindex = 0;
				cursoron = true;	
			} else {
				if (e.keyCode == 38) {
					currentindex--;
					if (currentindex<0) currentindex = 0;
				} else if (e.keyCode == 40) {
					currentindex++;
					if (currentindex>subjectfilterlist.length-1) currentindex = subjectfilterlist.length-1;
				}
			}
			subjectfilterlist.removeClass('listhighlight');
			jQuery(subjectfilterlist[currentindex]).addClass('listhighlight');
		} else if (e.keyCode == 13) { // Enter key
			if (!cursoron) {
				addfiltered()
			} else {
				inputfield.attr('value',jQuery(subjectfilterlist[currentindex]).text());
				addfiltered();
			}
			subjectfilter.empty();
			help.show();
			return false
		} else if (e.keyCode == 37 || e.keyCode == 39) { // 37 left 39 right
			cursoron = false
			currentindex = null
		} else if (e.keyCode == 27) { // Escape
			cursoron = false
			currentindex = null
			jQuery(this).blur();
		}
	}

	function addfiltered() {
		help.text(settings.helptext_initial);
		value = inputfield_cleaned();
		if (value != '' && subjectli().index(value) == -1) {
			subjectlist.append('<li><a class="symbol" href="#">&times;</a> '+value+'</li>');
			inputfield.attr('value', '')
			subjectlist.children('li').children('a').click(function(){return removeSubjectHandler(this)});
		}
		return false;
	}
	
	function removeSubjectHandler(target) {
		jQuery(target).parent().remove();
		return false;
	}
	
	function subjectli() {
		return subjectlist.children('li').map(function(i,v) {return jQuery(v).html().stripHTML().toLowerCase()});
	}
	
	function inputfield_cleaned() {
		return inputfield.attr('value').stripHTML().toLowerCase();
	}

	function highlight(html, match) {
		html = html.replace(new RegExp(match.escapeRegExp(), 'gi'), '<strong>'+match+'</strong>');
		return html;
	}
	
};
