function humaneDate(date_str){
	if (! this.re) {
		var datere = "(\\d{4})-?(\\d{2})-?(\\d{2})";
		var timere = "(\\d{2}):?(\\d{2}):?(\\d{2}(?:\\.\\d+)?)";
		var tzre = "(Z|(?:\\+|-)\\d{2}(?:\\:\\d{2})?)?";
		this.re = new RegExp("^" + datere + "[ T]" + timere + tzre + "$");
	}

	var matches = this.re.exec(date_str);
	if (! matches)
		return date_str;

	var year = matches[1];
	var month = matches[2] - 1;
	var day = matches[3];
	var hour = matches[4];
	var minute = matches[5];
	var second = Math.floor(matches[6]);
	//var ms = matches[6] - second;
	var tz = matches[7];
	var ms = 0;
	var offset = 0;

	if (tz && tz != "Z") {
		var tzmatches = tz.match(/^(\+|-)(\d{2})(\:(\d{2}))$/);
		if (tzmatches) {
			offset = Number(tzmatches[2]) * 60 + Number(tzmatches[4]);
			if (tzmatches[1] == "-")
				offset = -offset;
		}
	}
	offset *= 60 * 1000;
	var dateval = Date.UTC(year, month, day, hour, minute, second, ms) - offset;

	var time_formats = [
		[60, 'Just Now'],
		[90, '1 Minute'], // 60*1.5
		[3600, 'Minutes', 60], // 60*60, 60
		[5400, '1 Hour'], // 60*60*1.5
		[86400, 'Hours', 3600], // 60*60*24, 60*60
		[129600, '1 Day'], // 60*60*24*1.5
		[604800, 'Days', 86400], // 60*60*24*7, 60*60*24
		[907200, '1 Week'], // 60*60*24*7*1.5
		[2628000, 'Weeks', 604800], // 60*60*24*(365/12), 60*60*24*7
		[3942000, '1 Month'], // 60*60*24*(365/12)*1.5
		[31536000, 'Months', 2628000], // 60*60*24*365, 60*60*24*(365/12)
		[47304000, '1 Year'], // 60*60*24*365*1.5
		[3153600000, 'Years', 31536000], // 60*60*24*365*100, 60*60*24*365
		[4730400000, '1 Century'], // 60*60*24*365*100*1.5
	];

	var dt = new Date,
		seconds = (dt - dateval) / 1000, token = ' ago',
		i = 0,
		format;

	if (seconds < 0) {
		seconds = Math.abs(seconds);
		token = '';
	}

	while (format = time_formats[i++]) {
		if (seconds < format[0]) {
			if (format.length == 2) {
				return format[1] + (i > 1 ? token : ''); // Conditional so we don't return Just Now Ago
			} else {
				return Math.round(seconds / format[2]) + ' ' + format[1] + (i > 1 ? token : '');
			}
		}
	}

	// overflow for centuries
	if(seconds > 4730400000)
		return Math.round(seconds / 4730400000) + ' Centuries' + token;

	return date_str;
};

var chat = {

	data : {
		lastID 		: 0,
		noActivity	: 0
	},

	init : function(username){

		chat.data.jspAPI = $('#msgContainer').jScrollPane({ verticalDragMinHeight: 12, verticalDragMaxHeight: 12 }).data('jsp');
		chat.data.username = username;

		var working = false;

		$('#submitForm').submit(function() {
			var message = $('#chatText').val();

			if (message.length == 0)
				return false;
			if (working)
				return false;

			working = true;

			var tempID = 't'+Math.round(Math.random()*1000000),
				params = { id: tempID, uid: 1, username: chat.data.username, sex: '<img src="/img/icons/16x16/blank.png" />', flag: '', message: message.replace(/</g,'&lt;').replace(/>/g,'&gt;') };

			chat.addChatLine($.extend({}, params));

			$.wrapPOST('submitChat',$(this).serialize(),function(r){
				working = false;
				$('#chatText').val('');
				$('#chat-'+tempID).remove();

				params['id'] = r.insertID;
				chat.addChatLine($.extend({}, params));
			});

			return false;
		});

		(function getChatsTimeoutFunction(){
			chat.getChats(getChatsTimeoutFunction);
		})();
		(function getUsersTimeoutFunction(){
			chat.getUsers(getUsersTimeoutFunction);
		})();

	},

	render : function(template, params){
		var arr = [];
		switch(template) {
			case 'user':
				arr = [
					'<div class="user-list clearfix">',
					'<div class="ulist1">',params.user,'</div>',
					'<div class="ulist2"><img src="',params.flag,'" /></div>',
					'<div class="ulist3"><img src="',params.gender,'" /></div>',
					'<div class="ulist4">',params.age,'</div>',
					'<div class="ulist5">',params.location,'</div>',
					'<div class="ulist6">',params.career,'</div>',
					'</div>'
				];
				break;
			default:
				params.message = params.message.replace(/((f|ht)tps?:\/\/.+?(\s|$))/g, '<a href="'+'$1'+'" class="external" target="_blank">'+'$1'+'</a>');
				if (params.uid == 1)
				{
					arr = [
					'<div id="chat-',params.id,'" class="chat">',
					'<div class="user">',params.username,'</div>',
					'<div class="trunk"><div class="time">',params.time,'</div><div class="msg">',params.message,'</div></div>',
					'</div>'
					];
				}
				else
				{
					arr = [
					'<div id="chat-',params.id,'" class="chat">',
					'<div class="user">',params.flag,' ',params.sex,' <a href="profile.php?id=',params.uid,'">',params.username,'</a></div>',
					'<div class="trunk"><div class="time">',params.time,'</div><div class="msg">',params.message,'</div></div>',
					'</div>'
					];
				}
				break;
		}
		return arr.join('');
	},

	addChatLine : function(params){
		if (params.time)
		{
			params.time = humaneDate(params.time); //params.time = parseISO8601(params.time);
		}
		else
		{
			var d = new Date;
			params.time = d.toString().substring(4, 21);
		}

		var markup = chat.render('chat', params), exists = $('#chat-'+params.id);

		if (exists.length)
			exists.remove();

		if (!chat.data.lastID)
			$('#msgContainer p').remove();

		if (params.id.toString().charAt(0) != 't') {
			var previous = $('#chat-'+(params.id - 1));
			if (previous.length)
				previous.after(markup);
			else
				chat.data.jspAPI.getContentPane().append(markup);
		}
		else
			chat.data.jspAPI.getContentPane().append(markup);

		chat.data.jspAPI.reinitialise();
		chat.data.jspAPI.scrollToBottom(true);
	},

	getChats : function(callback){
		$.wrapGET('getChats', {lastID: chat.data.lastID}, function(r) {

			for (var i=0; i<r.chats.length; i++)
			{
				chat.addChatLine(r.chats[i]);
			}

			if (r.chats.length)
			{
				chat.data.noActivity = 0;
				chat.data.lastID = r.chats[i-1].id;
			}
			else
			{
				chat.data.noActivity++;
			}

			if (!chat.data.lastID)
			{
				chat.data.jspAPI.getContentPane().html('<p class="no-chat">No messages yet</p>');
			}

			var nextRequest = 2000;

			if (chat.data.noActivity > 2) { nextRequest = 5000; }
			if (chat.data.noActivity > 5) { nextRequest = 15000; }
			if (chat.data.noActivity > 10){ nextRequest = 30000; }
			setTimeout(callback,nextRequest);
		});
	},

	// Requesting a list with all the users.
	getUsers : function(callback){
		$.wrapGET('getUsers',function(r){
			var users = [];
			for (var i=0; i< r.users.length;i++){
				if (r.users[i]){
					users.push(chat.render('user',r.users[i]));
				}
			}
			var message = '';
			if (r.total < 1) { message = 'No one is online'; }
			else { message = r.total+' '+(r.total == 1 ? 'person':'people')+' online'; }
			users.push('<p class="count">'+message+'</p>');
			$('#userList').html(users.join(''));
			setTimeout(callback,90000);
		});
	}
};

// Custom GET & POST wrappers:

$.wrapPOST = function(action,data,callback){
	$.post('/ajax/chat.php?action='+action, data, callback, 'json');
}

$.wrapGET = function(action,data,callback){
	$.get('/ajax/chat.php?action='+action, data, callback, 'json');
}

