/*
 * Tokenizer text field. Facebook style username entry
 * most useful with jQuery.autocomplete
 *
 * 
 * options {
 *     input [optional]: selector of input field to use. one is created if none specified.
 *     autocomplete [optional]: selector of autocomplete field to attach to. usually the same as input.
 *     maxItems [optional]: 
 *     formatItem [required]: function(data){return '';} function which formats the display value of items.
 *     equal [required]: function(data1, data2){return data1==data2} function to determine equality of two items.
 * }
 * 
 * @author dreisch
 */

;(function($) {
	
$.fn.extend({
	tokenizer: function(options) {
		return this.each(function() {
			new $.Tokenizer(this, options);
		});
	},
	getTokenizer: function() {
		return this.triggerHandler("getTokenizer");
	}
});

})(jQuery);

$.Tokenizer = function Tokenizer(target, options) {
	var This = this;
	
	target = $(target);
	if (!options.input) {
		options.input = $('<input type="text" />');
		target.append(input);
	};
	var input = $(options.input);
	
	if (options.autocomplete) {
		$(options.autocomplete).bind('result', function(event, data) {
	    	This.addToken(data);
	    });
	}
	target.click(function() {
		input.focus();
	}).bind('getTokenizer', getTokenizer);
	
	input.keydown(onKeydown);
	
	var uniqueId = 0;
	var values = {};
	
	function getTokenizer() {
		return This;
	}
	
	//public
	this.addToken = function addToken(data) {
		var numItems = 0;
		$(input).val('').focus();
		for (var id in values) {
			++numItems;
			var value = values[id];
			// tokenizer should probably allow the user to define a comparator
			if (options.equal(value, data))
				return This;
		}
		if (numItems >= options.maxItems)
			return This;
		var id = ++uniqueId;
		var cont = target;
		var div = $('<div class="tokenizer_token"><span class="tokenizer_display"></span><span class="tokenizer_value"></span><div class="tokenizer_close"></div></div>');
		div.find('.tokenizer_close').click(function() {
			var myId = parseInt($(this).siblings('.tokenizer_value').html());
			This.removeToken(myId);
		});
		div.find('.tokenizer_display').html(options.formatItem(data));
	    input.before(' ').before(div);
		data._tokenizerId = id;
		div.find('.tokenizer_value').html(""+id);
		values[id] = data;
		$(target).trigger('add', id);
		$(target).trigger('change', id);
		return This;
	};
	
	//public
	this.removeToken = function removeToken(id) {
		if (!values[id])
			return;
		target.find('.tokenizer_value').each(function() {
			if (parseInt($(this).html()) == parseInt(id))
				$(this).parents('.tokenizer_token').remove();
		});
		delete values[id];
		$(target).trigger('remove', id);
		$(target).trigger('change', id);
		return This;
	};
	
	//public
	this.getValues = function getValues() {
		var ret = [];
		for (var id in values)
			ret.push(values[id]);
		return ret;
	};
	this.clear = function clear() {
		for (var id in values)
			this.removeToken(id);
	}
	function onKeydown(event) {
		switch (event.which)
		{
		case 8: // backspaec
			if (!input.val()) {
				var theId = parseInt($(this).prev('.tokenizer_token').find('.tokenizer_value').html());
				This.removeToken(theId);
			}
			break;
		}
	}
	
	//public
	this.toString = function toString() {
		return '[Tokenizer]';
	};
};


