TweetFmt = function(callback)
{
	this.callback = callback ? callback : function(type, buffer) { return buffer };
}
new TweetFmt();
TweetFmt.prototype = 
{
	"state": "CDAT",
	"buffer": "",
	"output": "",
	"input": "",
	"inputInd": 0,
	"inputLen": 0,
	"truncate": function(str, pos, append)
	{
		var len;
		
		str = str.replace(/<[^>]+>/g, " ");
		str = str.replace(/\&nbsp;/g, " ");
		
		len = str.length;

		if (pos >= len)
		{
			return str;
		}
		pos--;
		len--;
		
		while ((pos < len) &&
		       !str.substr(pos, 1).match(/\s/))
		{
			pos++;
		}
		
		if (pos < len)
		{
			return str.substr(0, pos)+append;
		}
		else
		{
			return str.substr(0, len + 1);
		}
	},
	"nextTok": function()
	{
		var i, c, ch, b = "";
		if ((i = this.inputInd) == (c = this.inputLen))
		{
			return null;
		}

		while (i < c)
		{
			ch = this.input.substr(i, 1);
			if (ch.match(/[:\/\@\#\.\s]/))
			{
				if (b.length)
				{
					this.inputInd = i;
					return b;
				}
				else
				{
					this.inputInd = i + 1;
					return ch;
				}
			}
			else
			{
				b += ch;
				i++;
			}
		}
		this.inputInd = i;
		return b;
	},
	"dumpBuffer": function(state)
	{
		this.output += this.buffer;
		this.buffer = "";
		if (state && state.length)
		{
			this.state.push(state);
		}
	},
	"bashURI": function()
	{
		var m, tb = "";
		while (m = this.buffer.match(/([^a-zA-Z0-9=\?\&\#])$/))
		{
			this.buffer = this.buffer.substr(0, this.buffer.length - 1);
			tb = m[1]+tb;
		}

		this.buffer = this.callback("URI", this.buffer)+tb;
		// '<a href="'+this.buffer+'" target="_blank">'+this.buffer+'<'+'/a>'+tb;
	},
	"bashTwitAt": function()
	{
		this.buffer = this.callback("AT", this.buffer);
		// '@<a href="http://twitter.com/'+this.buffer.substr(1)+'" target="_blank">'+this.buffer.substr(1)+'<'+'/a>';
	},
	"fmt": function(input, len)
	{
		var tok, state;
		this.input = len ? this.truncate(input, len, "...") : input;
		this.inputInd = 0;
		this.inputLen = input.length;
		this.output = "";
		this.buffer = "";
		this.state = ["CDAT"];
		while ((tok = this.nextTok()) !== null)
		{
			state = this.state.pop();
			switch (state)
			{
				case "CDAT":
					if (tok.toLowerCase().match(/https?/))
					{
						this.dumpBuffer("URI_PROTO");
					}
					else if (tok == "@")
					{
						this.dumpBuffer("TWIT_AT");
					}
					else
					{
						this.state.push(state);
					}
					break;


				case "TWIT_AT":
					if (!tok.match(/^[a-zA-Z0-9_\-]+$/))
					{
						this.bashTwitAt();
						this.dumpBuffer("CDAT");
					}
					else
					{
						this.state.push(state);
					}
					break;


				case "URI_PROTO":
					if (tok == ":")
					{
						this.state.push("URI_PROTO_COLON");
					}
					else
					{
						this.dumpBuffer("CDAT");
					}
					break;

				case "URI_PROTO_COLON":
					if (tok == "/")
					{
						this.state.push("URI_PROTO_SLASH");
					}
					else
					{
						this.dumpBuffer("CDAT");
					}
					break;

				case "URI_PROTO_SLASH":
					if (tok == "/")
					{
						this.state.push("URI_HOSTNAME");
					}
					else
					{
						this.dumpBuffer("CDAT");
					}
					break;

				case "URI_HOSTNAME":
					if (tok == "/")
					{
						this.state.push("URI_PATHNAME");
					}
					else if (tok.match(/[\s]/))
					{
						this.bashURI();
						this.dumpBuffer("CDAT");
					}
					else if (tok.match(/[a-zA-Z0-9_\-\.]/))
					{
						this.state.push(state);
					}
					else
					{
						this.dumpBuffer("CDAT");
					}
					break;

				case "URI_PATHNAME":
					if (tok.match(/[\s]/))
					{
						this.bashURI();
						this.dumpBuffer("CDAT");
					}
					else
					{
						this.state.push(state);
					}
					break;
			}
			
			this.buffer += tok;
		}

		switch (state)
		{
			case "URI_HOSTNAME":
			case "URI_PATHNAME":
				this.bashURI();
				break;
			case "TWIT_AT":
				this.bashTwitAt();
				break;
		}
		this.dumpBuffer("CDAT");
		return this.output;
	}
};
