function tokenizer_init(ajax_url, ajax_hash)
{
	url = ajax_url;
	hash = ajax_hash;
	current_tokenizer = null;
	highlighted_token = null;
	stop_ajax_update = false;
	
	$$('.tokenizer_input').each(function(tok) {
		var dropdown_wrapper = new Element('div', { 'class': 'dropdown_wrapper' });
		var dropdown = new Element('div', { 'class': 'dropdown' });
		tok.values = new Hash();
		tok.up().appendChild(dropdown_wrapper);
		dropdown_wrapper.appendChild(dropdown);
		empty_dropdown(dropdown);
		dropdown.hide();
		
		new Ajax.Request(url+'?hash='+hash+'&action=request&param='+tok.readAttribute('name'), {
			method: 'get',
			onSuccess: function(response) {
				response.responseJSON.each(function(row) {
					tokenize(tok,row.name,row.id);
				});
			}
		});
		
		tok.observe('focus', function(event) {
			dropdown.show();
		});
		
		tok.observe('blur', function(event) {
			dropdown.hide();
		});
		
		tok.observe('keydown', function(event) {
			switch(event.keyCode)
			{
				case 13:
					var selected_option = dropdown.childElements().find( function(o) {
						return o.hasClassName('option_highlighted');
					});
					if(!Object.isUndefined(selected_option))
					{
						var selected_ajax_id = selected_option.readAttribute('ajax_id');
						new Ajax.Request(url+'?hash='+hash+'&action=add&param='+tok.readAttribute('name')+'&value='+selected_ajax_id, {
							method: 'get',
							onSuccess: function() {
								tokenize(tok,selected_option.readAttribute('value'),selected_ajax_id);
							}
						});
					}
					empty_dropdown(dropdown);
					Event.stop(event);
					break;
				case 27:
					tok.blur();
					Event.stop(event);
					break;
				case 38:
					var dropdown_options = dropdown.childElements();
					var selected_option = dropdown_options.find( function(o) {
						return o.hasClassName('option_highlighted');
					});
					if(!Object.isUndefined(selected_option))
					{
						selected_option.removeClassName('option_highlighted').addClassName('option');
						var siblings = selected_option.previousSiblings();
						if(siblings.size() > 0)
						{
							siblings[0].removeClassName('option').addClassName('option_highlighted');
						}
						else
						{
							dropdown_options[dropdown_options.size()-1].removeClassName('option').addClassName('option_highlighted');
						}
					}
					break;
				case 9:
					dropdown.hide();
					hide_tokenizer();
					return;
				case 40:
					var dropdown_options = dropdown.childElements();
					var selected_option = dropdown_options.find( function(o) {
						return o.hasClassName('option_highlighted');
					});
					if(!Object.isUndefined(selected_option))
					{
						selected_option.removeClassName('option_highlighted').addClassName('option');
						var siblings = selected_option.nextSiblings();
						if(siblings.size() > 0)
						{
							siblings[0].removeClassName('option').addClassName('option_highlighted');
						}
						else
						{
							dropdown_options[0].removeClassName('option').addClassName('option_highlighted');
						}
					}
					return false;
					break;
			}
		});
		
		tok.observe('keyup', function(event) {
			switch(event.keyCode)
			{
				case 13: case 27: case 38: case 40:
					Event.stop(event);
					break;
				default:
					stop_ajax_update = false;
					if(tok.value.length > 0)
					{
						new Ajax.Request(url+'?hash='+hash+'&action=suggest&param='+tok.readAttribute('name')+'&key='+tok.value, {
							method: 'get',
							onSuccess: function(response) {
								if(!stop_ajax_update)
								{
									dropdown.childElements().invoke('remove');
									
									response.responseJSON.each(function(row)
									{
										if (!(tok.values && tok.values.get(row.name)))
											{
											var option = new Element('div', { 'class': 'option' });
											option.setAttribute('ajax_id',row.id);
											option.setAttribute('value',row.name);
											option.appendChild(document.createTextNode(row.name));
											dropdown.appendChild(option);
											option.observe('mousemove', function() {
												var selected_option = option.up().childElements().find( function(o) {
													return o.hasClassName('option_highlighted');
												});
												if(!Object.isUndefined(selected_option))
												{
													selected_option.removeClassName('option_highlighted').addClassName('option');
												}
												option.removeClassName('option').addClassName('option_highlighted');
											});
											option.observe('mousedown', function() {
												new Ajax.Request(url+'?hash='+hash+'&action=add&param='+tok.readAttribute('name')+'&value='+row.id, {
													method: 'get'
												});
												tokenize(tok,row.name,row.id);
												empty_dropdown(dropdown);
											});
										}
									});
									
									if(dropdown.childElements().size() == 0)
									{
										var option = new Element('div', { 'class': 'option' });
										option.appendChild(document.createTextNode('-- No Matches'));
										dropdown.appendChild(option);
									}
									else
									{
										dropdown.childElements()[0].removeClassName('option').addClassName('option_highlighted');
									}
								}
							}
						});
					}
					else
					{
						empty_dropdown(dropdown);
					}
					break;
			}
		});
	});

	document.observe('click', function(event) {
		var tokren = Event.element(event);
		if(tokren.hasClassName('token'))
		{
			hide_tokenizer();
			highlight_token(tokren);
		}
		else if(tokren.hasClassName('token_text'))
		{
			hide_tokenizer();
			highlight_token(tokren.up());
		}
		else
		{
			if(!tokren.hasClassName('input_tokenizer'))
			{
				tokren = tokren.up('.input_tokenizer');
			}
			if(Object.isUndefined(tokren))
			{
				hide_tokenizer();
			}
			else
			{
				show_tokenizer(tokren.down('.tokenizer_input'));
			}
			if(highlighted_token != null)
			{
				highlighted_token.removeClassName('highlighted_token').addClassName('token');
			}
		}
	});
	
	document.observe('keydown', function(event) {
		if(highlighted_token != null)
		{
			var previousSiblings = highlighted_token.previousSiblings();
			var nextSiblings = highlighted_token.nextSiblings();
			switch(event.keyCode)
			{
				case 8:
					remove_token(highlighted_token);
					if(previousSiblings.size() > 0)
					{
						highlight_token(previousSiblings[0]);
					}
					else
					{
						nextSiblings = nextSiblings[0];
						if(nextSiblings.hasClassName('token'))
						{
							highlight_token(nextSiblings);
						}
						else if(nextSiblings.hasClassName('tokenizer_input'))
						{
							show_tokenizer(nextSiblings);
						}
					}
					Event.stop(event);
					break;
				case 13:
					Event.stop(event);
					break;
				case 46:
					remove_token(highlighted_token);
					if(nextSiblings.size() > 0)
					{
						nextSiblings = nextSiblings[0];
						if(nextSiblings.hasClassName('token'))
						{
							highlight_token(nextSiblings);
						}
						else if(previousSiblings.size() > 0)
						{
							highlight_token(previousSiblings[0]);
						}
						else if(nextSiblings.hasClassName('tokenizer_input'))
						{
							show_tokenizer(nextSiblings);
						}
					}
					else if(previousSiblings.size() > 0)
					{
						highlight_token(previousSiblings[0]);
					}
					break;
				case 37:
					if(previousSiblings.size() > 0)
					{
						highlight_token(previousSiblings[0]);
					}
					break;
				case 39:
					nextSiblings = nextSiblings[0];
					if(nextSiblings.hasClassName('token'))
					{
						highlight_token(nextSiblings);
					}
					else if(nextSiblings.hasClassName('tokenizer_input'))
					{
						show_tokenizer(nextSiblings);
					}
					break;
			}
		}
	});
}

function ajax_add(url,tok,hash,name,id)
{
	new Ajax.Request(url+'?hash='+hash+'&action=add&param='+name+'&value='+id, {
		method: 'get',
		onSuccess: function() {
			tokenize(tok,selected_option.readAttribute('value'),selected_ajax_id);
		}
	});	
}

function hide_tokenizer()
{
	if(current_tokenizer != null)
	{
		current_tokenizer = null;
	}
}

function show_tokenizer(tok)
{
	if(tok != current_tokenizer)
	{
		current_tokenizer = tok;
		tok.focus();
	}
	if(highlighted_token != null)
	{
		highlighted_token.removeClassName('highlighted_token').addClassName('token');
		highlighted_token = null;
	}
}

function tokenize(tok,value,ajax_id)
{
	if (!(tok.values && tok.values.get(value)))
	{
		tok.values.set(value,true);
		var token = new Element('a', { 'class': 'token' });
		token.setAttribute('ajax_id',ajax_id);
		token.setAttribute('tokenizer_name',tok.readAttribute('name'));
		var token_text = new Element('span', { 'class': 'token_text' });
		token_text.update(value);
		token.appendChild(token_text);
		var token_x = new Element('span', { 'class': 'x_button' });
		token_x.appendChild(document.createTextNode('\u200b'));
		token_x.observe('click', function() {
			remove_token(token);
		});
		token.appendChild(token_x);
		tok.up().insertBefore(token,tok);
		tok.fire("tokenizer:change", { action : "add" , target : token.down().innerHTML } );
	}
	tok.value = '';
}

function empty_dropdown(dropdown)
{
	stop_ajax_update = true;
	dropdown.childElements().invoke('remove');                                               
	var option = new Element('div', { 'class': 'option' });
	option.appendChild(document.createTextNode('-- Enter A Keyword'));
	dropdown.appendChild(option);
}

function remove_token(token)
{
	if (!!token.next(".tokenizer_input").values.get(token.down().innerHTML))
	{
		if (token.next(".tokenizer_input").values) { 
			token.next(".tokenizer_input").values.set(token.down().innerHTML,false); 
			token.next(".tokenizer_input").fire("tokenizer:change", { action : "remove" , target : token.down().innerHTML } );
		}
		new Ajax.Request(url+'?hash='+hash+'&action=remove&param='+token.readAttribute('tokenizer_name')+'&value='+token.readAttribute('ajax_id'), {
			method: 'get'
		});
		token.up().removeChild(token);
	}
}

function highlight_token(token)
{
	if(highlighted_token != null)
	{
		highlighted_token.removeClassName('highlighted_token').addClassName('token');
	}
	token.removeClassName('token').addClassName('highlighted_token');
	highlighted_token = token;
}