/*

Jappix - An open social platform
These are the Jappix Mini JS scripts for Jappix

-------------------------------------------------

License: AGPL
Author: Vanaryon
Last revision: 04/08/11

*/

// Jappix Mini vars
var MINI_DISCONNECT	= false;
var MINI_AUTOCONNECT	= false;
var MINI_SHOWPANE	= false;
var MINI_INITIALIZED	= false;
var MINI_ANONYMOUS	= false;
var MINI_ANIMATE	= false;
var MINI_NICKNAME	= null;
var MINI_TITLE		= null;
var MINI_DOMAIN		= null;
var MINI_USER		= null;
var MINI_PASSWORD	= null;
var MINI_RECONNECT	= 0;
var MINI_GROUPCHATS	= [];
var MINI_PASSWORDS	= [];
var MINI_RESOURCE	= JAPPIX_RESOURCE + ' Mini';

// Setups connection handlers
function setupConMini(con) {
	con.registerHandler('message', handleMessageMini);
	con.registerHandler('presence', handlePresenceMini);
	con.registerHandler('iq', handleIQMini);
	con.registerHandler('onerror', handleErrorMini);
	con.registerHandler('onconnect', connectedMini);
}

// Connects the user with the given logins
function connectMini(domain, user, password) {
	try {
		// We define the http binding parameters
		oArgs = new Object();
		
		if(HOST_BOSH_MINI)
			oArgs.httpbase = HOST_BOSH_MINI;
		else
			oArgs.httpbase = HOST_BOSH;
		
		// We create the new http-binding connection
		con = new JSJaCHttpBindingConnection(oArgs);
		
		// And we handle everything that happen
		setupConMini(con);
		
		// Generate a resource
		var random_resource = getDB('jappix-mini', 'resource');
		
		if(!random_resource)
			random_resource = MINI_RESOURCE + ' (' + (new Date()).getTime() + ')';
		
		// We retrieve what the user typed in the login inputs
		oArgs = new Object();
		oArgs.secure = true;
		oArgs.xmllang = XML_LANG;
		oArgs.resource = random_resource;
		oArgs.domain = domain;
		
		// Store the resource (for reconnection)
		setDB('jappix-mini', 'resource', random_resource);
		
		// Anonymous login?
		if(MINI_ANONYMOUS) {
			// Anonymous mode disabled?
			if(!allowedAnonymous()) {
				logThis('Not allowed to use anonymous mode.', 2);
				
				// Notify this error
				notifyErrorMini();
				
				return false;
			}
			
			// Bad domain?
			else if(lockHost() && (domain != HOST_ANONYMOUS)) {
				logThis('Not allowed to connect to this anonymous domain: ' + domain, 2);
				
				// Notify this error
				notifyErrorMini();
				
				return false;
			}
			
			oArgs.authtype = 'saslanon';
		}
		
		// Normal login
		else {
			// Bad domain?
			if(lockHost() && (domain != HOST_MAIN)) {
				logThis('Not allowed to connect to this main domain: ' + domain, 2);
				
				// Notify this error
				notifyErrorMini();
				
				return false;
			}
			
			// No nickname?
			if(!MINI_NICKNAME)
				MINI_NICKNAME = user;
			
			oArgs.username = user;
			oArgs.pass = password;
		}
		
		// We connect !
		con.connect(oArgs);
		
		logThis('Jappix Mini is connecting...', 3);
	}
	
	catch(e) {
		// Logs errors
		logThis('Error while logging in: ' + e, 1);
		
		// Reset Jappix Mini
		disconnectedMini();
	}
	
	finally {
		return false;
	}
}

// When the user is connected
function connectedMini() {
	// Update the roster
	jQuery('#jappix_mini a.jm_pane.jm_button span.jm_counter').text('0');
	
	// Do not get the roster if anonymous
	if(MINI_ANONYMOUS)
		initializeMini();
	else
		getRosterMini();
	
	// For logger
	if(MINI_RECONNECT)
		logThis('Jappix Mini is now reconnected.', 3);
	else
		logThis('Jappix Mini is now connected.', 3);
	
	// Reset reconnect var
	MINI_RECONNECT = 0;
}

// When the user disconnects
function saveSessionMini() {
	// Not connected?
	if(!isConnected())
		return;
	
	// Save the actual Jappix Mini DOM
	setDB('jappix-mini', 'dom', jQuery('#jappix_mini').html());
	setDB('jappix-mini', 'nickname', MINI_NICKNAME);
	
	// Save the scrollbar position
	var scroll_position = '';
	var scroll_hash = jQuery('#jappix_mini div.jm_conversation:has(a.jm_pane.jm_clicked)').attr('data-hash');
	
	if(scroll_hash)
		scroll_position = document.getElementById('received-' + scroll_hash).scrollTop + '';
	
	setDB('jappix-mini', 'scroll', scroll_position);
	
	// Save the session stamp
	setDB('jappix-mini', 'stamp', getTimeStamp());
	
	// Suspend connection
	con.suspend(false);
	
	logThis('Jappix Mini session save tool launched.', 3);
}

// Disconnects the connected user
function disconnectMini() {
	// No connection?
	if(!isConnected())
		return false;
	
	// Change markers
	MINI_DISCONNECT = true;
	MINI_INITIALIZED = false;
	
	// Add disconnection handler
	con.registerHandler('ondisconnect', disconnectedMini);
	
	// Disconnect the user
	con.disconnect();
	
	logThis('Jappix Mini is disconnecting...', 3);
	
	return false;
}

// When the user is disconnected
function disconnectedMini() {
	// Remove the stored items
	removeDB('jappix-mini', 'dom');
	removeDB('jappix-mini', 'nickname');
	removeDB('jappix-mini', 'scroll');
	removeDB('jappix-mini', 'stamp');
	
	// Connection error?
	if(!MINI_DISCONNECT || MINI_INITIALIZED) {
		// Browser error?
		notifyErrorMini();
		
		// Reset reconnect timer
		jQuery('#jappix_mini').stopTime();
		
		// Try to reconnect after a while
		if(MINI_INITIALIZED && (MINI_RECONNECT < 5)) {
			// Reconnect interval
			var reconnect_interval = 10;
			
			if(MINI_RECONNECT)
				reconnect_interval = (5 + (5 * MINI_RECONNECT)) * 1000;
			
			MINI_RECONNECT++;
			
			// Set timer
			jQuery('#jappix_mini').oneTime(reconnect_interval, function() {
				launchMini(true, MINI_SHOWPANE, MINI_DOMAIN, MINI_USER, MINI_PASSWORD);
			});
		}
	}
	
	// Normal disconnection?
	else
		launchMini(false, MINI_SHOWPANE, MINI_DOMAIN, MINI_USER, MINI_PASSWORD);
	
	// Reset markers
	MINI_DISCONNECT = false;
	MINI_INITIALIZED = false;
	
	logThis('Jappix Mini is now disconnected.', 3);
}

// Handles the incoming messages
function handleMessageMini(msg) {
	var type = msg.getType();
	
	// This is a message Jappix can handle
	if((type == 'chat') || (type == 'normal') || (type == 'groupchat') || !type) {
		// Get the body
		var body = trim(msg.getBody());
		
		// Any subject?
		var subject = trim(msg.getSubject());
		
		if(subject)
			body = subject;
		
		if(body) {
			// Get the values
			var from = fullXID(getStanzaFrom(msg));
			var xid = bareXID(from);
			var use_xid = xid;
			var hash = hex_md5(xid);
			var nick = thisResource(from);
			
			// Read the delay
			var delay = readMessageDelay(msg.getNode());
			var d_stamp;
			
			// Manage this delay
			if(delay) {
				time = relativeDate(delay);
				d_stamp = Date.jab2date(delay);
			}
			
			else {
				time = getCompleteTime();
				d_stamp = new Date();
			}
			
			// Get the stamp
			var stamp = extractStamp(d_stamp);
			
			// Is this a groupchat private message?
			if(exists('#jappix_mini #chat-' + hash + '[data-type=groupchat]')) {
				// Regenerate some stuffs
				if((type == 'chat') || !type) {
					xid = from;
					hash = hex_md5(xid);
				}
				
				// XID to use for a groupchat
				else
					use_xid = from;
			}
			
			// Message type
			var message_type = 'user-message';
			
			// Grouphat values
			if(type == 'groupchat') {
				// Old message
				if(msg.getChild('delay', NS_URN_DELAY) || msg.getChild('x', NS_DELAY))
					message_type = 'old-message';
				
				// System message?
				if(!nick || subject) {
					nick = '';
					message_type = 'system-message';
				}
			}
			
			// Chat values
			else {
				nick = jQuery('#jappix_mini a#friend-' + hash).text().revertHtmlEnc();
				
				// No nickname?
				if(!nick)
					nick = getXIDNick(xid);
			}
			
			// Define the target div
			var target = '#jappix_mini #chat-' + hash;
			
			// Create the chat if it does not exist
			if(!exists(target) && (type != 'groupchat'))
				chatMini(type, xid, nick, hash);
			
			// Display the message
			displayMessageMini(type, body, use_xid, nick, hash, time, stamp, message_type);
			
			// Notify the user if not focused & the message is not a groupchat old one
			if((!jQuery(target + ' a.jm_chat-tab').hasClass('jm_clicked') || !isFocused()) && (message_type == 'user-message'))
				notifyMessageMini(hash);
			
			logThis('Message received from: ' + from);
		}
	}
}

// Handles the incoming IQs
function handleIQMini(iq) {
	// Define some variables
	var iqFrom = fullXID(getStanzaFrom(iq));
	var iqID = iq.getID();
	var iqQueryXMLNS = iq.getQueryXMLNS();
	var iqType = iq.getType();
	var iqNode = iq.getNode();
	
	// Build the response
	var iqResponse = new JSJaCIQ();
	
	iqResponse.setID(iqID);
	iqResponse.setTo(iqFrom);
	iqResponse.setType('result');
	
	// Software version query
	if((iqQueryXMLNS == NS_VERSION) && (iqType == 'get')) {
		/* REF: http://xmpp.org/extensions/xep-0092.html */
		
		var iqQuery = iqResponse.setQuery(NS_VERSION);
		
		iqQuery.appendChild(iq.buildNode('name', {'xmlns': NS_VERSION}, 'Jappix Mini'));
		iqQuery.appendChild(iq.buildNode('version', {'xmlns': NS_VERSION}, JAPPIX_VERSION));
		iqQuery.appendChild(iq.buildNode('os', {'xmlns': NS_VERSION}, BrowserDetect.OS));
		
		con.send(iqResponse);
		
		logThis('Received software version query: ' + iqFrom);
	}
	
	// Roster push
	else if((iqQueryXMLNS == NS_ROSTER) && (iqType == 'set')) {
		// Display the friend
		handleRosterMini(iq);
		
		con.send(iqResponse);
		
		logThis('Received a roster push.');
	}
	
	// Disco info query
	else if((iqQueryXMLNS == NS_DISCO_INFO) && (iqType == 'get')) {
		/* REF: http://xmpp.org/extensions/xep-0030.html */
		
		var iqQuery = iqResponse.setQuery(NS_DISCO_INFO);
		
		// We set the name of the client
		iqQuery.appendChild(iq.appendNode('identity', {
			'category': 'client',
			'type': 'web',
			'name': 'Jappix Mini',
			'xmlns': NS_DISCO_INFO
		}));
		
		// We set all the supported features
		var fArray = new Array(
			NS_DISCO_INFO,
			NS_VERSION,
			NS_ROSTER,
			NS_MUC,
			NS_VERSION,
			NS_URN_TIME
		);
		
		for(i in fArray)
			iqQuery.appendChild(iq.buildNode('feature', {'var': fArray[i], 'xmlns': NS_DISCO_INFO}));
		
		con.send(iqResponse);
		
		logThis('Received a disco#infos query.');
	}
	
	// User time query
	else if(jQuery(iqNode).find('time').size() && (iqType == 'get')) {
		/* REF: http://xmpp.org/extensions/xep-0202.html */
		
		var iqTime = iqResponse.appendNode('time', {'xmlns': NS_URN_TIME});
		iqTime.appendChild(iq.buildNode('tzo', {'xmlns': NS_URN_TIME}, getDateTZO()));
		iqTime.appendChild(iq.buildNode('utc', {'xmlns': NS_URN_TIME}, getXMPPTime('utc')));
		
		con.send(iqResponse);
		
		logThis('Received local time query: ' + iqFrom);
	}
}

// Handles the incoming errors
function handleErrorMini(err) {
	// First level error (connection error)
	if(jQuery(err).is('error')) {
		// Notify this error
		disconnectedMini();
		
		logThis('First level error received.', 1);
	}
}

// Handles the incoming presences
function handlePresenceMini(pr) {
	// Get the values
	var from = fullXID(getStanzaFrom(pr));
	var xid = bareXID(from);
	var resource = thisResource(from);
	var hash = hex_md5(xid);
	var type = pr.getType();
	var show = pr.getShow();
	
	// Manage the received presence values
	if((type == 'error') || (type == 'unavailable'))
		show = 'unavailable';
	
	else {
		switch(show) {
			case 'chat':
			case 'away':
			case 'xa':
			case 'dnd':
				break;
			
			default:
				show = 'available';
				
				break;
		}
	}
	
	// Is this a groupchat presence?
	var groupchat_path = '#jappix_mini #chat-' + hash + '[data-type=groupchat]';
	
	if(exists(groupchat_path)) {
		// Groupchat buddy presence (not me)
		if(resource != unescape(jQuery(groupchat_path).attr('data-nick'))) {
			// Regenerate some stuffs
			var groupchat = xid;
			xid = from;
			hash = hex_md5(xid);
			
			// Remove this from the roster
			if(show == 'unavailable')
				removeBuddyMini(hash, groupchat);
			
			// Add this to the roster
			else
				addBuddyMini(xid, hash, resource, groupchat);
		}
	}
	
	// Friend path
	var chat = '#jappix_mini #chat-' + hash;
	var friend = '#jappix_mini a#friend-' + hash;
	var send_input = chat + ' input.jm_send-messages';
	
	// Is this friend online?
	if(show == 'unavailable') {
		// Offline marker
		jQuery(friend).addClass('jm_offline').removeClass('jm_online');
		
		// Disable the chat tools
		jQuery(chat).addClass('jm_disabled');
		jQuery(send_input).attr('disabled', true).attr('data-value', _e("Unavailable")).val(_e("Unavailable"));
	}
	
	else {
		// Online marker
		jQuery(friend).removeClass('jm_offline').addClass('jm_online');
		
		// Enable the chat input
		jQuery(chat).removeClass('jm_disabled');
		jQuery(send_input).removeAttr('disabled').val('');
	}
	
	// Change the show presence of this buddy
	jQuery(friend + ' span.jm_presence, ' + chat + ' span.jm_presence').attr('class', 'jm_presence jm_images jm_' + show);
	
	// Update the presence counter
	updateRosterMini();
	
	logThis('Presence received from: ' + from);
}

// Handles the MUC main elements
function handleMUCMini(pr) {
	// We get the xml content
	var xml = pr.getNode();
	var from = fullXID(getStanzaFrom(pr));
	var room = bareXID(from);
	var hash = hex_md5(room);
	var resource = thisResource(from);
	
	// Is it a valid server presence?
	var valid = false;
	
	if(!resource || (resource == unescape(jQuery('#jappix_mini #chat-' + hash + '[data-type=groupchat]').attr('data-nick'))))
		valid = true;
	
	// Password required?
	if(valid && jQuery(xml).find('error[type=auth] not-authorized').size()) {
		// Create a new prompt
		openPromptMini(printf(_e("This room (%s) is protected with a password."), room));
		
		// When prompt submitted
		jQuery('#jappix_popup div.jm_prompt form').submit(function() {
			try {
				// Read the value
				var password = closePromptMini();
				
				// Any submitted chat to join?
				if(password) {
					// Send the password
					presenceMini('', '', '', '', from, password, true, handleMUCMini);
					
					// Focus on the pane again
					switchPaneMini('chat-' + hash, hash);
				}
			}
			
			catch(e) {}
			
			finally {
				return false;
			}
		});
		
		return;
	}
	
	// Nickname conflict?
	else if(valid && jQuery(xml).find('error[type=cancel] conflict').size()) {
		// New nickname
		var nickname = resource + '_';
		
		// Send the new presence
		presenceMini('', '', '', '', room + '/' + nickname, '', true, handleMUCMini);
		
		// Update the nickname marker
		jQuery('#jappix_mini #chat-' + hash).attr('data-nick', escape(nickname));
	}
	
	// Handle normal presence
	else
		handlePresenceMini(pr);
}

// Updates the user presence
function presenceMini(type, show, priority, status, to, password, limit_history, handler) {
	var pr = new JSJaCPresence();
	
	// Add the attributes
	if(to)
		pr.setTo(to);
	if(type)
		pr.setType(type);
	if(show)
		pr.setShow(show);
	if(priority)
		pr.setPriority(priority);
	if(status)
		pr.setStatus(status);
	
	// Special presence elements
	if(password || limit_history) {
		var x = pr.appendNode('x', {'xmlns': NS_MUC});
		
		// Any password?
		if(password)
			x.appendChild(pr.buildNode('password', {'xmlns': NS_MUC}, password));
		
		// Any history limit?
		if(limit_history)
			x.appendChild(pr.buildNode('history', {'maxstanzas': 10, 'seconds': 86400, 'xmlns': NS_MUC}));
	}
	
	// Send the packet
	if(handler)
		con.send(pr, handler);
	else
		con.send(pr);
	
	// No type?
	if(!type)
		type = 'available';
	
	logThis('Presence sent: ' + type, 3);
}

// Sends a given message
function sendMessageMini(aForm) {
	try {
		var body = trim(aForm.body.value);
		var xid = aForm.xid.value;
		var type = aForm.type.value;
		var hash = hex_md5(xid);
		
		if(body && xid) {
			// Send the message
			var aMsg = new JSJaCMessage();
			
			aMsg.setTo(xid);
			aMsg.setType(type);
			aMsg.setBody(body);
			
			con.send(aMsg);
			
			// Clear the input
			aForm.body.value = '';
			
			// Display the message we sent
			if(type != 'groupchat')
				displayMessageMini(type, body, getXID(), 'me', hash, getCompleteTime(), getTimeStamp(), 'user-message');
			
			logThis('Message (' + type + ') sent to: ' + xid);
		}
	}
	
	catch(e) {}
	
	finally {
		return false;
	}
}

// Generates the asked smiley image
function smileyMini(image, text) {
	return ' <img class="jm_smiley jm_smiley-' + image + ' jm_images" alt="' + encodeQuotes(text) + '" src="' + JAPPIX_STATIC + 'php/get.php?t=img&amp;f=others/blank.gif" /> ';
}

// Notifies incoming chat messages
function notifyMessageMini(hash) {
	// Define the paths
	var tab = '#jappix_mini #chat-' + hash + ' a.jm_chat-tab';
	var notify = tab + ' span.jm_notify';
	var notify_middle = notify + ' span.jm_notify_middle';
	
	// Notification box not yet added
	if(!exists(notify))
		jQuery(tab).append(
			'<span class="jm_notify">' + 
				'<span class="jm_notify_left jm_images"></span>' + 
				'<span class="jm_notify_middle">0</span>' + 
				'<span class="jm_notify_right jm_images"></span>' + 
			'</span>'
		);
	
	// Increment the notification number
	var number = parseInt(jQuery(notify_middle).text());
	jQuery(notify_middle).text(number + 1);
	
	// Change the page title
	notifyTitleMini();
}

// Notifies the user from a session error
function notifyErrorMini() {
	// Replace the Jappix Mini DOM content
	jQuery('#jappix_mini').html(
		'<div class="jm_starter">' + 
			'<a class="jm_pane jm_button jm_images" href="https://mini.jappix.com/issues" target="_blank" title="' + _e("Click here to solve the error") + '">' + 
				'<span class="jm_counter jm_error jm_images">' + _e("Error") + '</span>' + 
			'</a>' + 
		'</div>'
	);
}

// Updates the page title with the new notifications
function notifyTitleMini() {
	// No saved title? Abort!
	if(MINI_TITLE == null)
		return false;
	
	// Page title code
	var title = MINI_TITLE;
	
	// Count the number of notifications
	var number = 0;
	
	jQuery('#jappix_mini span.jm_notify span.jm_notify_middle').each(function() {
		number = number + parseInt(jQuery(this).text());
	});
	
	// No new stuffs? Reset the title!
	if(number)
		title = '[' + number + '] ' + title;
	
	// Apply the title
	document.title = title;
	
	return true;
}

// Clears the notifications
function clearNotificationsMini(hash) {
	// Not focused?
	if(!isFocused())
		return false;
	
	// Remove the notifications counter
	jQuery('#jappix_mini #chat-' + hash + ' span.jm_notify').remove();
	
	// Update the page title
	notifyTitleMini();
	
	return true;
}

// Updates the roster counter
function updateRosterMini() {
	jQuery('#jappix_mini a.jm_button span.jm_counter').text(jQuery('#jappix_mini a.jm_online').size());
}

// Creates the Jappix Mini DOM content
function createMini(domain, user, password) {
	// Try to restore the DOM
        var dom = getDB('jappix-mini', 'dom');
        var stamp = parseInt(getDB('jappix-mini', 'stamp'));
	var suspended = false;
	
	// Invalid stored DOM?
	if(dom && isNaN(jQuery(dom).find('a.jm_pane.jm_button span.jm_counter').text()))
		dom = null;
	
	// Can resume a session?
	con = new JSJaCHttpBindingConnection();
	setupConMini(con);
	
	// Old DOM?
	if(dom && ((getTimeStamp() - stamp) < JSJACHBC_MAX_WAIT) && con.resume()) {
		// Read the old nickname
		MINI_NICKNAME = getDB('jappix-mini', 'nickname');
		
		// Marker
		suspended = true;
	}
	
	// New DOM?
	else {
		dom =   '<div class="jm_position">' + 
				'<div class="jm_conversations"></div>' + 
				
				'<div class="jm_starter">' + 
					'<div class="jm_roster">' + 
						'<div class="jm_actions">' + 
							'<a class="jm_logo jm_images" href="https://mini.jappix.com/" target="_blank"></a>' + 
							'<a class="jm_one-action jm_join jm_images" title="' + _e("Join a chat") + '" href="#"></a>' + 
						'</div>' + 
						
						'<div class="jm_buddies"></div>' + 
					'</div>' + 
					
					'<a class="jm_pane jm_button jm_images" href="#">' + 
						'<span class="jm_counter jm_images">' + _e("Please wait...") + '</span>' + 
					'</a>' + 
				'</div>' + 
			'</div>';
	}
	
	// Create the DOM
	jQuery('body').append('<div id="jappix_mini">' + dom + '</div>');
	
	// Adapt roster height
	adaptRosterMini();
	
	// The click events
	jQuery('#jappix_mini a.jm_button').click(function() {
		// Using a try/catch override IE issues
		try {
			// Presence counter
			var counter = '#jappix_mini a.jm_pane.jm_button span.jm_counter';
			
			// Cannot open the roster?
			if(jQuery(counter).text() == _e("Please wait..."))
				return false;
			
			// Not yet connected?
			if(jQuery(counter).text() == _e("Chat")) {
				// Remove the animated bubble
				jQuery('#jappix_mini div.jm_starter span.jm_animate').stopTime().remove();
				
				// Add a waiting marker
				jQuery(counter).text(_e("Please wait..."));
				
				// Launch the connection!
				connectMini(domain, user, password);
				
				return false;
			}
			
			// Normal actions
			if(!jQuery(this).hasClass('jm_clicked'))
				showRosterMini();
			else
				hideRosterMini();
		}
		
		catch(e) {}
		
		finally {
			return false;
		}
	});
	
	jQuery('#jappix_mini div.jm_actions a.jm_join').click(function() {
		// Using a try/catch override IE issues
		try {
			// Create a new prompt
			openPromptMini(_e("Please enter the group chat address to join."));
			
			// When prompt submitted
			jQuery('#jappix_popup div.jm_prompt form').submit(function() {
				try {
					// Read the value
					var join_this = closePromptMini();
					
					// Any submitted chat to join?
					if(join_this) {
						// Get the chat room to join
						chat_room = bareXID(generateXID(join_this, 'groupchat'));
						
						// Create a new groupchat
						chatMini('groupchat', chat_room, getXIDNick(chat_room), hex_md5(chat_room));
					}
				}
				
				catch(e) {}
				
				finally {
					return false;
				}
			});
		}
		
		catch(e) {}
		
		finally {
			return false;
		}
	});
	
	// Hides the roster when clicking away of Jappix Mini
	jQuery(document).click(function(evt) {
		if(!jQuery(evt.target).parents('#jappix_mini').size() && !exists('#jappix_popup'))
			hideRosterMini();
	});
	
	// Hides all panes double clicking away of Jappix Mini
	jQuery(document).dblclick(function(evt) {
		if(!jQuery(evt.target).parents('#jappix_mini').size() && !exists('#jappix_popup'))
			switchPaneMini();
	});
	
	// Suspended session resumed?
	if(suspended) {
		// Initialized marker
		MINI_INITIALIZED = true;
		
		// Restore chat input values
		jQuery('#jappix_mini div.jm_conversation input.jm_send-messages').each(function() {
			var chat_value = jQuery(this).attr('data-value');
			
			if(chat_value)
				jQuery(this).val(chat_value);
		});
		
		// Restore buddy click events
		jQuery('#jappix_mini a.jm_friend').click(function() {
			// Using a try/catch override IE issues
			try {
				chatMini('chat', unescape(jQuery(this).attr('data-xid')), unescape(jQuery(this).attr('data-nick')), jQuery(this).attr('data-hash'));
			}
			
			catch(e) {}
			
			finally {
				return false;
			}
		});
		
		// Restore chat click events
		jQuery('#jappix_mini div.jm_conversation').each(function() {
			chatEventsMini(jQuery(this).attr('data-type'), unescape(jQuery(this).attr('data-xid')), jQuery(this).attr('data-hash'));
		});
		
		// Scroll down to the last message
		var scroll_hash = jQuery('#jappix_mini div.jm_conversation:has(a.jm_pane.jm_clicked)').attr('data-hash');
		var scroll_position = getDB('jappix-mini', 'scroll');
		
		// Any scroll position?
		if(scroll_position)
			scroll_position = parseInt(scroll_position);
		
		if(scroll_hash) {
			// Use a timer to override the DOM lag issue
			jQuery(document).oneTime(200, function() {
				messageScrollMini(scroll_hash, scroll_position);
			});
		}
		
		// Update title notifications
		notifyTitleMini();
	}
	
	// Can auto-connect?
	else if(MINI_AUTOCONNECT)
		connectMini(domain, user, password);
	
	// Cannot auto-connect?
	else {
		// Chat text
		jQuery('#jappix_mini a.jm_pane.jm_button span.jm_counter').text(_e("Chat"));
		
		// Must animate?
		if(MINI_ANIMATE) {
			// Add content
			jQuery('#jappix_mini div.jm_starter').prepend(
				'<span class="jm_animate jm_images_animate"></span>'
			);
			
			// IE6 makes the image blink when animated...
			if((BrowserDetect.browser == 'Explorer') && (BrowserDetect.version < 7))
				return;
			
			// Add timers
			var anim_i = 0;
			
			jQuery('#jappix_mini div.jm_starter span.jm_animate').everyTime(10, function() {
				// Next
				anim_i++;
				
				// Margins
				var m_top = Math.cos(anim_i * 0.02) * 3;
				var m_left = Math.sin(anim_i * 0.02) * 3;
				
				// Apply new position!
				jQuery(this).css('margin-top', m_top + 'px')
				            .css('margin-left', m_left + 'px');
			});
		}
	}
}

// Displays a given message
function displayMessageMini(type, body, xid, nick, hash, time, stamp, message_type) {
	// Generate path
	var path = '#chat-' + hash;
	
	// Can scroll?
	var cont_scroll = document.getElementById('received-' + hash);
	var can_scroll = false;
	
	if(!cont_scroll.scrollTop || ((cont_scroll.clientHeight + cont_scroll.scrollTop) == cont_scroll.scrollHeight))
		can_scroll = true;
	
	// Remove the previous message border if needed
	var last = jQuery(path + ' div.jm_group:last');
	var last_stamp = parseInt(last.attr('data-stamp'));
	var last_b = jQuery(path + ' b:last');
	var last_xid = last_b.attr('data-xid');
	var last_type = last.attr('data-type');
	var grouped = false;
	var header = '';
	
	if((last_xid == xid) && (message_type == last_type) && ((stamp - last_stamp) <= 1800))
		grouped = true;
	
	else {
		// Write the message date
		if(nick)
			header += '<span class="jm_date">' + time + '</span>';
		
		// Write the buddy name at the top of the message group
		if(type == 'groupchat')
			header += '<b style="color: ' + generateColor(nick) + ';" data-xid="' + encodeQuotes(xid) + '">' + nick.htmlEnc() + '</b>';
		else if(nick == 'me')
			header += '<b class="jm_me" data-xid="' + encodeQuotes(xid) + '">' + _e("You") + '</b>';
		else
			header += '<b class="jm_him" data-xid="' + encodeQuotes(xid) + '">' + nick.htmlEnc() + '</b>';
	}
	
	// Apply the /me command
	var me_command = false;
	
	if(body.match(/^\/me /i)) {
		body = body.replace(/^\/me /i, nick + ' ');
		
		// Marker
		me_command = true;
	}
	
	// HTML-encode the message
	body = body.htmlEnc();
	
	// Apply the smileys
	body = body.replace(/(;-?\))(\s|$)/gi, smileyMini('wink', '$1'))
	           .replace(/(:-?3)(\s|$)/gi, smileyMini('waii', '$1'))
	           .replace(/(:-?\()(\s|$)/gi, smileyMini('unhappy', '$1'))
	           .replace(/(:-?P)(\s|$)/gi, smileyMini('tongue', '$1'))
	           .replace(/(:-?O)(\s|$)/gi, smileyMini('surprised', '$1'))
	           .replace(/(:-?\))(\s|$)/gi, smileyMini('smile', '$1'))
	           .replace(/(\^_?\^)(\s|$)/gi, smileyMini('happy', '$1'))
	           .replace(/(:-?D)(\s|$)/gi, smileyMini('grin', '$1'));
	
	// Filter the links
	body = applyLinks(body, 'mini');
	
	// Generate the message code
	if(me_command)
		body = '<em>' + body + '</em>';
	
	body = '<p>' + body + '</p>';
	
	// Create the message
	if(grouped)
		jQuery('#jappix_mini #chat-' + hash + ' div.jm_received-messages div.jm_group:last').append(body);
	else
		jQuery('#jappix_mini #chat-' + hash + ' div.jm_received-messages').append('<div class="jm_group jm_' + message_type + '" data-type="' + message_type + '" data-stamp="' + stamp + '">' + header + body + '</div>');
	
	// Scroll to this message
	if(can_scroll)
		messageScrollMini(hash);
}

// Switches to a given point
function switchPaneMini(element, hash) {
	// Hide every item
	jQuery('#jappix_mini a.jm_pane').removeClass('jm_clicked');
	jQuery('#jappix_mini div.jm_roster, #jappix_mini div.jm_chat-content').hide();
	
	// Show the asked element
	if(element && (element != 'roster')) {
		var current = '#jappix_mini #' + element;
		
		jQuery(current + ' a.jm_pane').addClass('jm_clicked');
		jQuery(current + ' div.jm_chat-content').show();
		
		// Use a timer to override the DOM lag issue
		jQuery(document).oneTime(10, function() {
			jQuery(current + ' input.jm_send-messages').focus();
		});
		
		// Scroll to the last message
		if(hash)
			messageScrollMini(hash);
	}
}

// Scrolls to the last chat message
function messageScrollMini(hash, position) {
	var id = document.getElementById('received-' + hash);
	
	// No defined position?
	if(!position)
		position = id.scrollHeight;
	
	id.scrollTop = position;
}

// Prompts the user with a given text
function openPromptMini(text, value) {
	// Initialize
	var prompt = '#jappix_popup div.jm_prompt';
	var input = prompt + ' form input';
	var value_input = input + '[type=text]';
	
	// Remove the existing prompt
	closePromptMini();
	
	// Add the prompt
	jQuery('body').append(
		'<div id="jappix_popup">' + 
			'<div class="jm_prompt">' + 
				'<form>' + 
					text + 
					'<input class="jm_text" type="text" value="" />' + 
					'<input class="jm_submit" type="submit" value="' + _e("Submit") + '" />' + 
					'<input class="jm_submit" type="reset" value="' + _e("Cancel") + '" />' + 
					'<div class="jm_clear"></div>' + 
				'</form>' + 
			'</div>' + 
		'</div>'
	);
	
	// Vertical center
	var vert_pos = '-' + ((jQuery(prompt).height() / 2) + 10) + 'px';
	jQuery(prompt).css('margin-top', vert_pos);
	
	// Apply the value?
	if(value)
		jQuery(value_input).val(value);
	
	// Focus on the input
	jQuery(document).oneTime(10, function() {
		jQuery(value_input).focus();
	});
	
	// Cancel event
	jQuery(input + '[type=reset]').click(function() {
		try {
			closePromptMini();
		}
		
		catch(e) {}
		
		finally {
			return false;
		}
	});
}

// Returns the prompt value
function closePromptMini() {
	// Read the value
	var value = jQuery('#jappix_popup div.jm_prompt form input').val();
	
	// Remove the popup
	jQuery('#jappix_popup').remove();
	
	return value;
}

// Manages and creates a chat
function chatMini(type, xid, nick, hash, pwd, show_pane) {
	var current = '#jappix_mini #chat-' + hash;
	
	// Not yet added?
	if(!exists(current)) {
		// Groupchat nickname
		if(type == 'groupchat') {
			var nickname = MINI_NICKNAME;
			
			// No nickname?
			if(!nickname) {
				// Create a new prompt
				openPromptMini(printf(_e("Please enter your nickname to join %s."), xid));
				
				// When prompt submitted
				jQuery('#jappix_popup div.jm_prompt form').submit(function() {
					try {
						// Read the value
						var nickname = closePromptMini();
						
						// Update the stored one
						if(nickname)
							MINI_NICKNAME = nickname;
						
						// Launch it again!
						chatMini(type, xid, nick, hash, pwd);
					}
					
					catch(e) {}
					
					finally {
						return false;
					}
				});
				
				return;
			}
		}
		
		// Create the HTML markup
		var html = '<div class="jm_conversation jm_type_' + type + '" id="chat-' + hash + '" data-xid="' + escape(xid) + '" data-type="' + type + '" data-nick="' + escape(nick) + '" data-hash="' + hash + '" data-origin="' + escape(cutResource(xid)) + '">' + 
				'<div class="jm_chat-content">' + 
					'<div class="jm_actions">' + 
						'<span class="jm_nick">' + nick + '</span>';
		
		// Check if the groupchat exists
		var groupchat_exists = false;
		
		if(MINI_GROUPCHATS && MINI_GROUPCHATS.length) {
			for(g in MINI_GROUPCHATS) {
				if(xid == bareXID(generateXID(MINI_GROUPCHATS[g], 'groupchat'))) {
					groupchat_exists = true;
					
					break;
				}
			}
		}
		
		// Any close button to display?
		if(((type == 'groupchat') && !groupchat_exists) || (type != 'groupchat'))
			html += '<a class="jm_one-action jm_close jm_images" title="' + _e("Close") + '" href="#"></a>';
		
		html += '</div>' + 
			
			'<div class="jm_received-messages" id="received-' + hash + '"></div>' + 
				'<form action="#" method="post">' + 
					'<input type="text" class="jm_send-messages" name="body" autocomplete="off" />' + 
					'<input type="hidden" name="xid" value="' + xid + '" />' + 
					'<input type="hidden" name="type" value="' + type + '" />' + 
				'</form>' + 
			'</div>' + 
			
			'<a class="jm_pane jm_chat-tab jm_images" href="#">' + 
				'<span class="jm_name">' + nick.htmlEnc() + '</span>' + 
			'</a>' + 
		'</div>';
		
		jQuery('#jappix_mini div.jm_conversations').prepend(html);
		
		// Get the presence of this friend
		if(type != 'groupchat') {
			var selector = jQuery('#jappix_mini a#friend-' + hash + ' span.jm_presence');
			
			// Default presence
			var show = 'available';
			
			// Read the presence
			if(selector.hasClass('jm_unavailable'))
				show = 'unavailable';
			else if(selector.hasClass('jm_chat'))
				show = 'chat';
			else if(selector.hasClass('jm_away'))
				show = 'away';
			else if(selector.hasClass('jm_xa'))
				show = 'xa';
			else if(selector.hasClass('jm_dnd'))
				show = 'dnd';
			
			// Create the presence marker
			jQuery(current + ' a.jm_chat-tab').prepend('<span class="jm_presence jm_images jm_' + show + '"></span>');
		}
		
		// The click events
		chatEventsMini(type, xid, hash);
		
		// Join the groupchat
		if(type == 'groupchat') {
			// Add the nickname value
			jQuery(current).attr('data-nick', escape(nickname));
			
			// Send the first groupchat presence
			presenceMini('', '', '', '', xid + '/' + nickname, pwd, true, handleMUCMini);
		}
	}
	
	// Focus on our pane
	if(show_pane != false)
		jQuery(document).oneTime(10, function() {
			switchPaneMini('chat-' + hash, hash);
		});
	
	return false;
}

// Events on the chat tool
function chatEventsMini(type, xid, hash) {
	var current = '#jappix_mini #chat-' + hash;
	
	// Submit the form
	jQuery(current + ' form').submit(function() {
		return sendMessageMini(this);
	});
	
	// Click on the tab
	jQuery(current + ' a.jm_chat-tab').click(function() {
		// Using a try/catch override IE issues
		try {
			// Not yet opened: open it!
			if(!jQuery(this).hasClass('jm_clicked')) {
				// Show it!
				switchPaneMini('chat-' + hash, hash);
				
				// Clear the eventual notifications
				clearNotificationsMini(hash);
			}
			
			// Yet opened: close it!
			else
				switchPaneMini();
		}
		
		catch(e) {}
		
		finally {
			return false;
		}
	});
	
	// Click on the close button
	jQuery(current + ' a.jm_close').click(function() {
		// Using a try/catch override IE issues
		try {
			jQuery(current).remove();
			
			// Quit the groupchat?
			if(type == 'groupchat') {
				// Send an unavailable presence
				presenceMini('unavailable', '', '', '', xid + '/' + unescape(jQuery(current).attr('data-nick')));
				
				// Remove this groupchat!
				removeGroupchatMini(xid);
			}
		}
		
		catch(e) {}
		
		finally {
			return false;
		}
	});
	
	// Click on the chat content
	jQuery(current + ' div.jm_received-messages').click(function() {
		try {
			jQuery(document).oneTime(10, function() {
				jQuery(current + ' input.jm_send-messages').focus();
			});
		}
		
		catch(e) {}
	});
	
	// Focus on the chat input
	jQuery(current + ' input.jm_send-messages').focus(function() {
		clearNotificationsMini(hash);
	})
	
	// Change on the chat input
	.keyup(function() {
		jQuery(this).attr('data-value', jQuery(this).val());
	});
}

// Shows the roster
function showRosterMini() {
	switchPaneMini('roster');
	jQuery('#jappix_mini div.jm_roster').show();
	jQuery('#jappix_mini a.jm_button').addClass('jm_clicked');
}

// Hides the roster
function hideRosterMini() {
	jQuery('#jappix_mini div.jm_roster').hide();
	jQuery('#jappix_mini a.jm_button').removeClass('jm_clicked');
}

// Removes a groupchat from DOM
function removeGroupchatMini(xid) {
	// Remove the groupchat private chats & the groupchat buddies from the roster
	jQuery('#jappix_mini div.jm_conversation[data-origin=' + escape(cutResource(xid)) + '], #jappix_mini div.jm_roster div.jm_grouped[data-xid=' + escape(xid) + ']').remove();
	
	// Update the presence counter
	updateRosterMini();
}

// Initializes Jappix Mini
function initializeMini() {
	// Update the marker
	MINI_INITIALIZED = true;
	
	// Send the initial presence
	if(!MINI_ANONYMOUS)
		presenceMini();
	
	// Join the groupchats
	for(var i = 0; i < MINI_GROUPCHATS.length; i++) {
		// Empty value?
		if(!MINI_GROUPCHATS[i])
			continue;
		
		// Using a try/catch override IE issues
		try {
			// Current chat room
			var chat_room = bareXID(generateXID(MINI_GROUPCHATS[i], 'groupchat'));
			
			// Open the current chat
			chatMini('groupchat', chat_room, getXIDNick(chat_room), hex_md5(chat_room), MINI_PASSWORDS[i], MINI_SHOWPANE);
		}
		
		catch(e) {}
	}
	
	// Must show the roster?
	if(!MINI_AUTOCONNECT && !MINI_GROUPCHATS.length)
		jQuery(document).oneTime(10, function() {
			showRosterMini();
		});
}

// Displays a roster buddy
function addBuddyMini(xid, hash, nick, groupchat) {
	// Element
	var element = '#jappix_mini a.jm_friend#friend-' + hash;
	
	// Yet added?
	if(exists(element))
		return false;
	
	// Generate the path
	var path = '#jappix_mini div.jm_roster div.jm_buddies';
	
	// Groupchat buddy
	if(groupchat) {
		// Generate the groupchat group path
		path = '#jappix_mini div.jm_roster div.jm_grouped[data-xid=' + escape(groupchat) + ']';
		
		// Must add a groupchat group?
		if(!exists(path)) {
			jQuery('#jappix_mini div.jm_roster div.jm_buddies').append(
				'<div class="jm_grouped" data-xid="' + escape(groupchat) + '">' + 
					'<div class="jm_name">' + getXIDNick(groupchat).htmlEnc() + '</div>' + 
				'</div>'
			);
		}
	}
	
	// Append this buddy content
	var code = '<a class="jm_friend jm_offline" id="friend-' + hash + '" data-xid="' + escape(xid) + '" data-nick="' + escape(nick) +  '" data-hash="' + hash + '" href="#"><span class="jm_presence jm_images jm_unavailable"></span>' + nick.htmlEnc() + '</a>';
	
	if(groupchat)
		jQuery(path).append(code);
	else
		jQuery(path).prepend(code);
	
	// Click event on this buddy
	jQuery(element).click(function() {
		// Using a try/catch override IE issues
		try {
			chatMini('chat', xid, nick, hash);
		}
		
		catch(e) {}
		
		finally {
			return false;
		}
	});
	
	return true;
}

// Removes a roster buddy
function removeBuddyMini(hash, groupchat) {
	// Remove the buddy from the roster
	jQuery('#jappix_mini a.jm_friend#friend-' + hash).remove();
	
	// Empty group?
	var group = '#jappix_mini div.jm_roster div.jm_grouped[data-xid=' + escape(groupchat) + ']';
	
	if(groupchat && !jQuery(group + ' a.jm_friend').size())
		jQuery(group).remove();
	
	return true;
}

// Gets the user's roster
function getRosterMini() {
	var iq = new JSJaCIQ();
	iq.setType('get');
	iq.setQuery(NS_ROSTER);
	con.send(iq, handleRosterMini);
	
	logThis('Getting roster...', 3);
}

// Handles the user's roster
function handleRosterMini(iq) {
	// Parse the roster
	jQuery(iq.getQuery()).find('item').each(function() {
		// Get the values
		var current = jQuery(this);
		var xid = current.attr('jid');
		var subscription = current.attr('subscription');
		
		// Not a gateway
		if(!isGateway(xid)) {
			var nick = current.attr('name');
			var hash = hex_md5(xid);
			
			// No name is defined?
			if(!nick)
				nick = getXIDNick(xid);
			
			// Action on the current buddy
			if(subscription == 'remove')
				removeBuddyMini(hash);
			else
				addBuddyMini(xid, hash, nick);
		}
	});
	
	// Not yet initialized
	if(!MINI_INITIALIZED)
		initializeMini();
	
	logThis('Roster got.', 3);
}

// Adapts the roster height to the window
function adaptRosterMini() {
	// Process the new height
	var height = jQuery(window).height() - 70;
	
	// Apply the new height
	jQuery('#jappix_mini div.jm_roster div.jm_buddies').css('max-height', height);
}

// Plugin launcher
function launchMini(autoconnect, show_pane, domain, user, password) {
	// Save infos to reconnect
	MINI_DOMAIN = domain;
	MINI_USER = user;
	MINI_PASSWORD = password;
	
	// Anonymous mode?
	if(!user || !password)
		MINI_ANONYMOUS = true;
	else
		MINI_ANONYMOUS = false;
	
	// Autoconnect (only if storage available to avoid floods)?
	if(autoconnect && hasDB())
		MINI_AUTOCONNECT = true;
	else
		MINI_AUTOCONNECT = false;
	
	// Show pane?
	if(show_pane)
		MINI_SHOWPANE = true;
	else
		MINI_SHOWPANE = false;
	
	// Remove Jappix Mini
	jQuery('#jappix_mini').remove();
	
	// Reconnect?
	if(MINI_RECONNECT) {
		logThis('Trying to reconnect (try: ' + MINI_RECONNECT + ')!');
		
		return createMini(domain, user, password);
	}
	
	// Append the Mini stylesheet
	jQuery('head').append('<link rel="stylesheet" href="' + JAPPIX_STATIC + 'php/get.php?t=css&amp;g=mini.xml" type="text/css" media="all" />');
	
	// Legacy IE stylesheet
	if((BrowserDetect.browser == 'Explorer') && (BrowserDetect.version < 7))
		jQuery('head').append('<link rel="stylesheet" href="' + JAPPIX_STATIC + 'php/get.php?t=css&amp;f=mini-ie.css" type="text/css" media="all" />');
	
	// Disables the browser HTTP-requests stopper
	jQuery(document).keydown(function(e) {
		if((e.keyCode == 27) && !isDeveloper())
			return false;
	});
	
	// Save the page title
	MINI_TITLE = document.title;
	
	// Sets the good roster max-height
	jQuery(window).resize(adaptRosterMini);
	
	// Logouts when Jappix is closed
	if(BrowserDetect.browser == 'Opera') {
		// Emulates onbeforeunload on Opera (link clicked)
		jQuery('a[href]:not([onclick])').click(function() {
			// Link attributes
			var href = jQuery(this).attr('href') || '';
			var target = jQuery(this).attr('target') || '';
			
			// Not new window or JS link
			if(href && !href.match(/^#/i) && !target.match(/_blank|_new/i))
				saveSessionMini();
		});
		
		// Emulates onbeforeunload on Opera (form submitted)
		jQuery('form:not([onsubmit])').submit(saveSessionMini);
	}
	
	jQuery(window).bind('beforeunload', saveSessionMini);
	
	// Create the Jappix Mini DOM content
	createMini(domain, user, password);
	
	logThis('Welcome to Jappix Mini! Happy coding in developer mode!');
}