--- /dev/null
+/*
+
+Jappix - An open social platform
+These are the presence JS scripts for Jappix
+
+-------------------------------------------------
+
+License: AGPL
+Author: Vanaryon
+Last revision: 23/09/11
+
+*/
+
+// Sends the user first presence
+var FIRST_PRESENCE_SENT = false;
+
+function firstPresence(checksum) {
+ logThis('First presence sent.', 3);
+
+ // Jappix is now ready: change the title
+ pageTitle('talk');
+
+ // Anonymous check
+ var is_anonymous = isAnonymous();
+
+ // Update our marker
+ FIRST_PRESENCE_SENT = true;
+
+ // Try to use the last status message
+ var status = getDB('options', 'presence-status');
+
+ if(!status)
+ status = '';
+
+ // We tell the world that we are online
+ if(!is_anonymous)
+ sendPresence('', '', '', status, checksum);
+
+ // Any status to apply?
+ if(status)
+ $('#presence-status').val(status);
+
+ // Enable the presence picker
+ $('#presence-status').removeAttr('disabled');
+ $('#my-infos .f-presence a.picker').removeClass('disabled');
+
+ // We set the last activity stamp
+ PRESENCE_LAST_ACTIVITY = getTimeStamp();
+
+ // We store our presence
+ setDB('presence-show', 1, 'available');
+
+ // Not anonymous
+ if(!is_anonymous) {
+ // We get the stored bookmarks (because of the photo hash and some other stuffs, we must get it later)
+ getStorage(NS_BOOKMARKS);
+
+ // We open a new chat if a XMPP link was submitted
+ if((parent.location.hash != '#OK') && LINK_VARS['x']) {
+ // A link is submitted in the URL
+ xmppLink(LINK_VARS['x']);
+
+ // Set a OK status
+ parent.location.hash = 'OK';
+ }
+ }
+}
+
+// Handles incoming presence packets
+function handlePresence(presence) {
+ // We define everything needed here
+ var from = fullXID(getStanzaFrom(presence));
+ var hash = hex_md5(from);
+ var node = presence.getNode();
+ var xid = bareXID(from);
+ var xidHash = hex_md5(xid);
+
+ // We get the type content
+ var type = presence.getType();
+ if(!type)
+ type = '';
+
+ // We get the priority content
+ var priority = presence.getPriority() + '';
+ if(!priority || (type == 'error'))
+ priority = '0';
+
+ // We get the show content
+ var show = presence.getShow();
+ if(!show || (type == 'error'))
+ show = '';
+
+ // We get the status content
+ var status = presence.getStatus();
+ if(!status || (type == 'error'))
+ status = '';
+
+ // We get the photo content
+ var photo = $(node).find('x[xmlns=' + NS_VCARD_P + ']:first photo');
+ var checksum = photo.text();
+ var hasPhoto = photo.size();
+
+ if(hasPhoto && (type != 'error'))
+ hasPhoto = 'true';
+ else
+ hasPhoto = 'false';
+
+ // We get the CAPS content
+ var caps = $(node).find('c[xmlns=' + NS_CAPS + ']:first').attr('ver');
+ if(!caps || (type == 'error'))
+ caps = '';
+
+ // This presence comes from another resource of my account with a difference avatar checksum
+ if((xid == getXID()) && (hasPhoto == 'true') && (checksum != getDB('checksum', 1)))
+ getAvatar(getXID(), 'force', 'true', 'forget');
+
+ // This presence comes from a groupchat
+ if(isPrivate(xid)) {
+ var x_muc = $(node).find('x[xmlns=' + NS_MUC_USER + ']:first');
+ var item = x_muc.find('item');
+ var affiliation = item.attr('affiliation');
+ var role = item.attr('role');
+ var reason = item.find('reason').text();
+ var iXID = item.attr('jid');
+ var iNick = item.attr('nick');
+ var nick = thisResource(from);
+ var messageTime = getCompleteTime();
+ var notInitial = true;
+
+ // Read the status code
+ var status_code = new Array();
+
+ x_muc.find('status').each(function() {
+ status_code.push(parseInt($(this).attr('code')));
+ });
+
+ // If this is an initial presence (when user join the room)
+ if(exists('#' + xidHash + '[data-initial=true]'))
+ notInitial = false;
+
+ // If one user is quitting
+ if(type && (type == 'unavailable')) {
+ displayMucPresence(from, xidHash, hash, type, show, status, affiliation, role, reason, status_code, iXID, iNick, messageTime, nick, notInitial);
+
+ removeDB('presence', from);
+ }
+
+ // If one user is joining
+ else {
+ // Fixes M-Link first presence bug (missing ID!)
+ if((nick == getMUCNick(xidHash)) && (presence.getID() == null) && !exists('#page-engine #' + xidHash + ' .list .' + hash)) {
+ handleMUC(presence);
+
+ logThis('Passed M-Link MUC first presence handling.', 2);
+ }
+
+ else {
+ displayMucPresence(from, xidHash, hash, type, show, status, affiliation, role, reason, status_code, iXID, iNick, messageTime, nick, notInitial);
+
+ var xml = '<presence from="' + encodeQuotes(from) + '"><priority>' + priority.htmlEnc() + '</priority><show>' + show.htmlEnc() + '</show><type>' + type.htmlEnc() + '</type><status>' + status.htmlEnc() + '</status><avatar>' + hasPhoto.htmlEnc() + '</avatar><checksum>' + checksum.htmlEnc() + '</checksum><caps>' + caps.htmlEnc() + '</caps></presence>';
+
+ setDB('presence', from, xml);
+ }
+ }
+
+ // Manage the presence
+ presenceFunnel(from, hash);
+ }
+
+ // This presence comes from an user or a gateway
+ else {
+ // Subscribed & unsubscribed stanzas
+ if((type == 'subscribed') || (type == 'unsubscribed'))
+ return;
+
+ // Subscribe stanza
+ else if(type == 'subscribe') {
+ // This is a buddy we can safely authorize, because we added him to our roster
+ if(exists('#buddy-list .buddy[data-xid=' + escape(xid) + ']'))
+ acceptSubscribe(xid);
+
+ // We do not know this entity, we'd be better ask the user
+ else {
+ // Get the nickname
+ var nickname = $(node).find('nick[xmlns=' + NS_NICK + ']:first').text();
+
+ // New notification
+ newNotification('subscribe', xid, [xid, nickname], status);
+ }
+ }
+
+ // Unsubscribe stanza
+ else if(type == 'unsubscribe')
+ sendRoster(xid, 'remove');
+
+ // Other stanzas
+ else {
+ // Unavailable/error presence
+ if(type == 'unavailable')
+ removeDB('presence', from);
+
+ // Other presence (available, subscribe...)
+ else {
+ var xml = '<presence from="' + encodeQuotes(from) + '"><priority>' + priority.htmlEnc() + '</priority><show>' + show.htmlEnc() + '</show><type>' + type.htmlEnc() + '</type><status>' + status.htmlEnc() + '</status><avatar>' + hasPhoto.htmlEnc() + '</avatar><checksum>' + checksum.htmlEnc() + '</checksum><caps>' + caps.htmlEnc() + '</caps></presence>';
+
+ setDB('presence', from, xml);
+ }
+
+ // We manage the presence
+ presenceFunnel(xid, xidHash);
+
+ // We display the presence in the current chat
+ if(exists('#' + xidHash)) {
+ var dStatus = filterStatus(xid, status, false);
+
+ if(dStatus)
+ dStatus = ' (' + dStatus + ')';
+
+ // Generate the presence-in-chat code
+ var dName = getBuddyName(from).htmlEnc();
+ var dBody = dName + ' (' + from + ') ' + _e("is now") + ' ' + humanShow(show, type) + dStatus;
+
+ // Check whether it has been previously displayed
+ var can_display = true;
+
+ if($('#' + xidHash + ' .one-line.system-message:last').html() == dBody)
+ can_display = false;
+
+ if(can_display)
+ displayMessage('chat', xid, xidHash, dName, dBody, getCompleteTime(), getTimeStamp(), 'system-message', false);
+ }
+ }
+ }
+
+ // For logger
+ if(!show) {
+ if(!type)
+ show = 'available';
+ else
+ show = 'unavailable';
+ }
+
+ logThis('Presence received: ' + show + ', from ' + from);
+}
+
+// Displays a MUC presence
+function displayMucPresence(from, roomHash, hash, type, show, status, affiliation, role, reason, status_code, iXID, iNick, messageTime, nick, initial) {
+ // Generate the values
+ var thisUser = '#page-engine #' + roomHash + ' .list .' + hash;
+ var thisPrivate = $('#' + hash + ' .message-area');
+ var nick_html = nick.htmlEnc();
+ var real_xid = '';
+ var write = nick_html + ' ';
+ var notify = false;
+
+ // Reset data?
+ if(!role)
+ role = 'participant';
+ if(!affiliation)
+ affiliation = 'none';
+
+ // Must update the role?
+ if(exists(thisUser) && (($(thisUser).attr('data-role') != role) || ($(thisUser).attr('data-affiliation') != affiliation)))
+ $(thisUser).remove();
+
+ // Any XID submitted?
+ if(iXID) {
+ real_xid = ' data-realxid="' + iXID + '"';
+ iXID = bareXID(iXID);
+ write += ' (<a onclick="return checkChatCreate(\'' + encodeOnclick(iXID) + '\', \'chat\');" href="xmpp:' + encodeOnclick(iXID) + '">' + iXID + '</a>) ';
+ }
+
+ // User does not exists yet
+ if(!exists(thisUser) && (!type || (type == 'available'))) {
+ var myself = '';
+
+ // Is it me?
+ if(nick == getMUCNick(roomHash)) {
+ // Enable the room
+ $('#' + roomHash + ' .message-area').removeAttr('disabled');
+
+ // Marker
+ myself = ' myself';
+ }
+
+ // Set the user in the MUC list
+ $('#' + roomHash + ' .list .' + role + ' .title').after(
+ '<div class="user ' + hash + myself + '" data-xid="' + encodeQuotes(from) + '" data-nick="' + escape(nick) + '"' + real_xid + ' data-role="' + encodeQuotes(role) + '" data-affiliation="' + encodeQuotes(affiliation) + '">' +
+ '<div class="name talk-images available">' + nick_html + '</div>' +
+
+ '<div class="avatar-container">' +
+ '<img class="avatar" src="' + './img/others/default-avatar.png' + '" alt="" />' +
+ '</div>' +
+ '</div>'
+ );
+
+ // Click event
+ if(nick != getMUCNick(roomHash))
+ $(thisUser).live('click', function() {
+ checkChatCreate(from, 'private');
+ });
+
+ // We tell the user that someone entered the room
+ if(!initial) {
+ notify = true;
+ write += _e("joined the chat room");
+
+ // Any status?
+ if(status)
+ write += ' (' + filterThisMessage(status, nick_html, true) + ')';
+ else
+ write += ' (' + _e("no status") + ')';
+ }
+
+ // Enable the private chat input
+ thisPrivate.removeAttr('disabled');
+ }
+
+ else if((type == 'unavailable') || (type == 'error')) {
+ // Is it me?
+ if(nick == getMUCNick(roomHash)) {
+ $(thisUser).remove();
+
+ // Disable the groupchat input
+ $('#' + roomHash + ' .message-area').attr('disabled', true);
+
+ // Remove all the groupchat users
+ $('#' + roomHash + ' .list .user').remove();
+ }
+
+ // Someone has been kicked or banned?
+ if(existArrayValue(status_code, 301) || existArrayValue(status_code, 307)) {
+ $(thisUser).remove();
+ notify = true;
+
+ // Kicked?
+ if(existArrayValue(status_code, 307))
+ write += _e("has been kicked");
+
+ // Banned?
+ if(existArrayValue(status_code, 301))
+ write += _e("has been banned");
+
+ // Any reason?
+ if(reason)
+ write += ' (' + filterThisMessage(reason, nick_html, true) + ')';
+ else
+ write += ' (' + _e("no reason") + ')';
+ }
+
+ // Nickname change?
+ else if(existArrayValue(status_code, 303) && iNick) {
+ notify = true;
+ write += printf(_e("changed his/her nickname to %s"), iNick.htmlEnc());
+
+ // New values
+ var new_xid = cutResource(from) + '/' + iNick;
+ var new_hash = hex_md5(new_xid);
+ var new_class = 'user ' + new_hash;
+
+ if($(thisUser).hasClass('myself'))
+ new_class += ' myself';
+
+ // Die the click event
+ $(thisUser).die('click');
+
+ // Change to the new nickname
+ $(thisUser).attr('data-nick', iNick)
+ .attr('data-xid', new_xid)
+ .find('.name').text(iNick);
+
+ // Change the user class
+ $(thisUser).attr('class', new_class);
+
+ // New click event
+ $('#page-engine #' + roomHash + ' .list .' + new_hash).live('click', function() {
+ checkChatCreate(new_xid, 'private');
+ });
+ }
+
+ // We tell the user that someone left the room
+ else if(!initial) {
+ $(thisUser).remove();
+ notify = true;
+ write += _e("left the chat room");
+
+ // Any status?
+ if(status)
+ write += ' (' + filterThisMessage(status, nick_html, true) + ')';
+ else
+ write += ' (' + _e("no status") + ')';
+ }
+
+ // Disable the private chat input
+ thisPrivate.attr('disabled', true);
+ }
+
+ // Must notify something
+ if(notify)
+ displayMessage('groupchat', from, roomHash, nick_html, write, messageTime, getTimeStamp(), 'system-message', false);
+
+ // Set the good status show icon
+ switch(show) {
+ case 'chat':
+ case 'away':
+ case 'xa':
+ case 'dnd':
+ break;
+
+ default:
+ show = 'available';
+ break;
+ }
+
+ $(thisUser + ' .name').attr('class', 'name talk-images ' + show);
+
+ // Set the good status text
+ var uTitle = nick;
+
+ // Any XID to add?
+ if(iXID)
+ uTitle += ' (' + iXID + ')';
+
+ // Any status to add?
+ if(status)
+ uTitle += ' - ' + status;
+
+ $(thisUser).attr('title', uTitle);
+
+ // Show or hide the role category, depending of its content
+ $('#' + roomHash + ' .list .role').each(function() {
+ if($(this).find('.user').size())
+ $(this).show();
+ else
+ $(this).hide();
+ });
+}
+
+// Filters a given status
+function filterStatus(xid, status, cut) {
+ var dStatus = '';
+
+ if(!status)
+ status = '';
+
+ else {
+ if(cut)
+ dStatus = truncate(status, 50);
+ else
+ dStatus = status;
+
+ dStatus = filterThisMessage(dStatus, getBuddyName(xid).htmlEnc(), true);
+ }
+
+ return dStatus;
+}
+
+// Displays a user's presence
+function displayPresence(value, type, show, status, hash, xid, avatar, checksum, caps) {
+ // Display the presence in the roster
+ var path = '#buddy-list .' + hash;
+ var buddy = $('#buddy-list .content .' + hash);
+ var dStatus = filterStatus(xid, status, false);
+ var tStatus = encodeQuotes(status);
+ var biStatus;
+
+ // The buddy presence behind his name
+ $(path + ' .name .buddy-presence').replaceWith('<p class="buddy-presence talk-images ' + type + '">' + value + '</p>');
+
+ // The buddy presence in the buddy infos
+ if(dStatus)
+ biStatus = dStatus;
+ else
+ biStatus = value;
+
+ $(path + ' .bi-status').replaceWith('<p class="bi-status talk-images ' + type + '" title="' + tStatus + '">' + biStatus + '</p>');
+
+ // When the buddy disconnect himself, we hide him
+ if((type == 'unavailable') || (type == 'error')) {
+ // Set a special class to the buddy
+ buddy.addClass('hidden-buddy');
+
+ // No filtering is launched?
+ if(!SEARCH_FILTERED)
+ buddy.hide();
+
+ // All the buddies are shown?
+ if(BLIST_ALL)
+ buddy.show();
+
+ // Chat stuffs
+ if(exists('#' + hash)) {
+ // Remove the chatstate stuffs
+ resetChatState(hash);
+ $('#' + hash + ' .chatstate').remove();
+ $('#' + hash + ' .message-area').removeAttr('data-chatstates');
+
+ // Get the buddy avatar (only if a chat is opened)
+ getAvatar(xid, 'cache', 'true', 'forget');
+ }
+ }
+
+ // If the buddy is online
+ else {
+ // When the buddy is online, we show it
+ buddy.removeClass('hidden-buddy');
+
+ // No filtering is launched?
+ if(!SEARCH_FILTERED)
+ buddy.show();
+
+ // Get the online buddy avatar if not a gateway
+ getAvatar(xid, 'cache', avatar, checksum);
+ }
+
+ // Display the presence in the chat
+ if(exists('#' + hash)) {
+ // We generate a well formed status message
+ if(dStatus) {
+ // No need to write the same status two times
+ if(dStatus == value)
+ dStatus = '';
+ else
+ dStatus = ' (' + dStatus + ')';
+ }
+
+ // We show the presence value
+ $('#' + hash + ' .bc-infos').replaceWith('<p class="bc-infos" title="' + tStatus + '"><span class="' + type + ' show talk-images">' + value + '</span>' + dStatus + '</p>');
+
+ // Process the new status position
+ adaptChatPresence(hash);
+
+ // Get the disco#infos for this user
+ var highest = getHighestResource(xid);
+
+ if(highest)
+ getDiscoInfos(highest, caps);
+ else
+ displayDiscoInfos(xid, '');
+ }
+
+ // Display the presence in the switcher
+ if(exists('#page-switch .' + hash))
+ $('#page-switch .' + hash + ' .icon').removeClass('available unavailable error away busy').addClass(type);
+
+ // Update roster groups
+ if(!SEARCH_FILTERED)
+ updateGroups();
+ else
+ funnelFilterBuddySearch();
+}
+
+// Process the chat presence position
+function adaptChatPresence(hash) {
+ // Get values
+ var pep_numb = $('#' + hash + ' .bc-pep').find('a').size();
+
+ // Process the right position
+ var presence_right = 12;
+
+ if(pep_numb)
+ presence_right = (pep_numb * 20) + 18;
+
+ // Apply the right position
+ $('#' + hash + ' p.bc-infos').css('right', presence_right);
+}
+
+// Convert the presence "show" element into a human-readable output
+function humanShow(show, type) {
+ if(type == 'unavailable')
+ show = _e("Unavailable");
+
+ else if(type == 'error')
+ show = _e("Error");
+
+ else {
+ switch(show) {
+ case 'chat':
+ show = _e("Talkative");
+ break;
+
+ case 'away':
+ show = _e("Away");
+ break;
+
+ case 'xa':
+ show = _e("Not available");
+ break;
+
+ case 'dnd':
+ show = _e("Busy");
+ break;
+
+ default:
+ show = _e("Available");
+ break;
+ }
+ }
+
+ return show;
+}
+
+// Makes the presence data go in the right way
+function presenceIA(type, show, status, hash, xid, avatar, checksum, caps) {
+ // Is there a status defined?
+ if(!status)
+ status = humanShow(show, type);
+
+ // Then we can handle the events
+ if(type == 'error')
+ displayPresence(_e("Error"), 'error', show, status, hash, xid, avatar, checksum, caps);
+
+ else if(type == 'unavailable')
+ displayPresence(_e("Unavailable"), 'unavailable', show, status, hash, xid, avatar, checksum, caps);
+
+ else {
+ switch(show) {
+ case 'chat':
+ displayPresence(_e("Talkative"), 'available', show, status, hash, xid, avatar, checksum, caps);
+ break;
+
+ case 'away':
+ displayPresence(_e("Away"), 'away', show, status, hash, xid, avatar, checksum, caps);
+ break;
+
+ case 'xa':
+ displayPresence(_e("Not available"), 'busy', show, status, hash, xid, avatar, checksum, caps);
+ break;
+
+ case 'dnd':
+ displayPresence(_e("Busy"), 'busy', show, status, hash, xid, avatar, checksum, caps);
+ break;
+
+ default:
+ displayPresence(_e("Available"), 'available', show, status, hash, xid, avatar, checksum, caps);
+ break;
+ }
+ }
+}
+
+// Gets the highest resource priority for an user
+function highestPriority(xid) {
+ var maximum = null;
+ var selector, priority, type, highest;
+
+ // This is a groupchat presence
+ if(xid.indexOf('/') != -1)
+ highest = XMLFromString(getDB('presence', xid));
+
+ // This is a "normal" presence: get the highest priority resource
+ else {
+ for(var i = 0; i < sessionStorage.length; i++) {
+ // Get the pointer values
+ var current = sessionStorage.key(i);
+
+ // If the pointer is on a stored presence
+ if(explodeThis('_', current, 0) == 'presence') {
+ // Get the current XID
+ var now = bareXID(explodeThis('_', current, 1));
+
+ // If the current XID equals the asked XID
+ if(now == xid) {
+ var xml = XMLFromString(sessionStorage.getItem(current));
+ var priority = parseInt($(xml).find('priority').text());
+
+ // Higher priority
+ if((priority >= maximum) || (maximum == null)) {
+ maximum = priority;
+ highest = xml;
+ }
+ }
+ }
+ }
+ }
+
+ // The user might be offline if no highest
+ if(!highest)
+ highest = XMLFromString('<presence><type>unavailable</type></presence>');
+
+ return highest;
+}
+
+// Gets the resource from a XID which has the highest priority
+function getHighestResource(xid) {
+ var xml = $(highestPriority(xid));
+ var highest = xml.find('presence').attr('from');
+ var type = xml.find('type').text();
+
+ // If the use is online, we can return its highest resource
+ if(!type || (type == 'available') || (type == 'null'))
+ return highest;
+ else
+ return false;
+}
+
+// Makes something easy to process for the presence IA
+function presenceFunnel(xid, hash) {
+ // Get the highest priority presence value
+ var xml = $(highestPriority(xid));
+ var type = xml.find('type').text();
+ var show = xml.find('show').text();
+ var status = xml.find('status').text();
+ var avatar = xml.find('avatar').text();
+ var checksum = xml.find('checksum').text();
+ var caps = xml.find('caps').text();
+
+ // Display the presence with that stored value
+ if(!type && !show)
+ presenceIA('', 'available', status, hash, xid, avatar, checksum, caps);
+ else
+ presenceIA(type, show, status, hash, xid, avatar, checksum, caps);
+}
+
+// Sends a defined presence packet
+function sendPresence(to, type, show, status, checksum, limit_history, password, handle) {
+ // Get some stuffs
+ var priority = getDB('priority', 1);
+
+ if(!priority)
+ priority = '1';
+ if(!checksum)
+ checksum = getDB('checksum', 1);
+ if(show == 'available')
+ show = '';
+ if(type == 'available')
+ type = '';
+
+ // New presence
+ var presence = new JSJaCPresence();
+
+ // Avoid "null" or "none" if nothing stored
+ if(!checksum || (checksum == 'none'))
+ checksum = '';
+
+ // Presence headers
+ if(to)
+ presence.setTo(to);
+ if(type)
+ presence.setType(type);
+ if(show)
+ presence.setShow(show);
+ if(status)
+ presence.setStatus(status);
+
+ presence.setPriority(priority);
+
+ // CAPS (entity capabilities)
+ presence.appendNode('c', {'xmlns': NS_CAPS, 'hash': 'sha-1', 'node': 'https://www.jappix.com/', 'ver': myCaps()});
+
+ // Nickname
+ var nickname = getName();
+
+ if(nickname)
+ presence.appendNode('nick', {'xmlns': NS_NICK}, nickname);
+
+ // vcard-temp:x:update node
+ var x = presence.appendNode('x', {'xmlns': NS_VCARD_P});
+ x.appendChild(presence.buildNode('photo', {'xmlns': NS_VCARD_P}, checksum));
+
+ // MUC X data
+ if(limit_history || password) {
+ var xMUC = presence.appendNode('x', {'xmlns': NS_MUC});
+
+ // Max messages age (for MUC)
+ if(limit_history)
+ xMUC.appendChild(presence.buildNode('history', {'maxstanzas': 20, 'seconds': 86400, 'xmlns': NS_MUC}));
+
+ // Room password
+ if(password)
+ xMUC.appendChild(presence.buildNode('password', {'xmlns': NS_MUC}, password));
+ }
+
+ // If away, send a last activity time
+ if((show == 'away') || (show == 'xa')) {
+ /* REF: http://xmpp.org/extensions/xep-0256.html */
+
+ presence.appendNode(presence.buildNode('query', {
+ 'xmlns': NS_LAST,
+ 'seconds': getPresenceLast()
+ }));
+ }
+
+ // Else, set a new last activity stamp
+ else
+ PRESENCE_LAST_ACTIVITY = getTimeStamp();
+
+ // Send the presence packet
+ if(handle)
+ con.send(presence, handle);
+ else
+ con.send(presence);
+
+ if(!type)
+ type = 'available';
+
+ logThis('Presence sent: ' + type, 3);
+}
+
+// Performs all the actions to get the presence data
+function presenceSend(checksum, autoidle) {
+ // We get the values of the inputs
+ var show = getUserShow();
+ var status = getUserStatus();
+
+ // Send the presence
+ if(!isAnonymous())
+ sendPresence('', '', show, status, checksum);
+
+ // We set the good icon
+ presenceIcon(show);
+
+ // We store our presence
+ if(!autoidle)
+ setDB('presence-show', 1, show);
+
+ // We send the presence to our active MUC
+ $('.page-engine-chan[data-type=groupchat]').each(function() {
+ var tmp_nick = $(this).attr('data-nick');
+
+ if(!tmp_nick)
+ return;
+
+ var room = unescape($(this).attr('data-xid'));
+ var nick = unescape(tmp_nick);
+
+ // Must re-initialize?
+ if(RESUME)
+ getMUC(room, nick);
+
+ // Not disabled?
+ else if(!$(this).find('.message-area').attr('disabled'))
+ sendPresence(room + '/' + nick, '', show, status, '', true);
+ });
+}
+
+// Changes the presence icon
+function presenceIcon(value) {
+ $('#my-infos .f-presence a.picker').attr('data-value', value);
+}
+
+// Sends a subscribe stanza
+function sendSubscribe(to, type) {
+ var status = '';
+
+ // Subscribe request?
+ if(type == 'subscribe')
+ status = printf(_e("Hi, I am %s, I would like to add you as my friend."), getName());
+
+ sendPresence(to, type, '', status);
+}
+
+// Accepts the subscription from another entity
+function acceptSubscribe(xid, name) {
+ // We update our chat
+ $('#' + hex_md5(xid) + ' .tools-add').hide();
+
+ // We send a subsribed presence (to confirm)
+ sendSubscribe(xid, 'subscribed');
+
+ // We send a subscription request (subscribe both sides)
+ sendSubscribe(xid, 'subscribe');
+
+ // Specify the buddy name (if any)
+ if(name)
+ sendRoster(xid, '', name)
+}
+
+// Sends automatic away presence
+var AUTO_IDLE = false;
+
+function autoIdle() {
+ // Not connected?
+ if(!isConnected())
+ return;
+
+ // Stop if an xa presence was set manually
+ var last_presence = getUserShow();
+
+ if(!AUTO_IDLE && ((last_presence == 'away') || (last_presence == 'xa')))
+ return;
+
+ var idle_presence;
+ var activity_limit;
+
+ // Can we extend to auto extended away mode (20 minutes)?
+ if(AUTO_IDLE && (last_presence == 'away')) {
+ idle_presence = 'xa';
+ activity_limit = 1200;
+ }
+
+ // We must set the user to auto-away (10 minutes)
+ else {
+ idle_presence = 'away';
+ activity_limit = 600;
+ }
+
+ // The user is really inactive and has set another presence than extended away
+ if(((!AUTO_IDLE && (last_presence != 'away')) || (AUTO_IDLE && (last_presence == 'away'))) && (getLastActivity() >= activity_limit)) {
+ // Then tell we use an auto presence
+ AUTO_IDLE = true;
+
+ // Get the old status message
+ var status = getDB('options', 'presence-status');
+
+ if(!status)
+ status = '';
+
+ // Change the presence input
+ $('#my-infos .f-presence a.picker').attr('data-value', idle_presence);
+ $('#presence-status').val(status);
+
+ // Then send the xa presence
+ presenceSend('', true);
+
+ logThis('Auto-idle presence sent: ' + idle_presence, 3);
+ }
+}
+
+// Restores the old presence on a document bind
+function eventIdle() {
+ // If we were idle, restore our old presence
+ if(AUTO_IDLE) {
+ // Get the values
+ var show = getDB('presence-show', 1);
+ var status = getDB('options', 'presence-status');
+
+ // Change the presence input
+ $('#my-infos .f-presence a.picker').attr('data-value', show);
+ $('#presence-status').val(status);
+ $('#presence-status').placeholder();
+
+ // Then restore the old presence
+ presenceSend('', true);
+
+ if(!show)
+ show = 'available';
+
+ logThis('Presence restored: ' + show, 3);
+ }
+
+ // Apply some values
+ AUTO_IDLE = false;
+ LAST_ACTIVITY = getTimeStamp();
+}
+
+// Lives the auto idle functions
+function liveIdle() {
+ // Apply the autoIdle function every minute
+ AUTO_IDLE = false;
+ $('#my-infos .f-presence').everyTime('30s', autoIdle);
+
+ // On body bind (click & key event)
+ $('body').live('mousedown', eventIdle)
+ .live('mousemove', eventIdle)
+ .live('keydown', eventIdle);
+}
+
+// Kills the auto idle functions
+function dieIdle() {
+ // Remove the event detector
+ $('body').die('mousedown', eventIdle)
+ .die('mousemove', eventIdle)
+ .die('keydown', eventIdle);
+}
+
+// Gets the user presence show
+function getUserShow() {
+ return $('#my-infos .f-presence a.picker').attr('data-value');
+}
+
+// Gets the user presence status
+function getUserStatus() {
+ return $('#presence-status').val();
+}
+
+// Plugin launcher
+function launchPresence() {
+ // Click event for user presence show
+ $('#my-infos .f-presence a.picker').click(function() {
+ // Disabled?
+ if($(this).hasClass('disabled'))
+ return false;
+
+ // Initialize some vars
+ var path = '#my-infos .f-presence div.bubble';
+ var show_id = ['xa', 'away', 'available'];
+ var show_lang = [_e("Not available"), _e("Away"), _e("Available")];
+ var show_val = getUserShow();
+
+ // Yet displayed?
+ var can_append = true;
+
+ if(exists(path))
+ can_append = false;
+
+ // Add this bubble!
+ showBubble(path);
+
+ if(!can_append)
+ return false;
+
+ // Generate the HTML code
+ var html = '<div class="bubble removable">';
+
+ for(i in show_id) {
+ // Yet in use: no need to display it!
+ if(show_id[i] == show_val)
+ continue;
+
+ html += '<a href="#" class="talk-images" data-value="' + show_id[i] + '" title="' + show_lang[i] + '"></a>';
+ }
+
+ html += '</div>';
+
+ // Append the HTML code
+ $('#my-infos .f-presence').append(html);
+
+ // Click event
+ $(path + ' a').click(function() {
+ // Update the presence show marker
+ $('#my-infos .f-presence a.picker').attr('data-value', $(this).attr('data-value'));
+
+ // Close the bubble
+ closeBubbles();
+
+ // Focus on the status input
+ $(document).oneTime(10, function() {
+ $('#presence-status').focus();
+ });
+
+ return false;
+ });
+
+ return false;
+ });
+
+ // Submit events for user presence status
+ $('#presence-status').placeholder()
+
+ .keyup(function(e) {
+ if(e.keyCode == 13) {
+ $(this).blur();
+
+ return false;
+ }
+ })
+
+ .blur(function() {
+ // Read the parameters
+ var show = getUserShow();
+ var status = getUserStatus();
+
+ // Read the old parameters
+ var old_show = getDB('presence-show', 1);
+ var old_status = getDB('options', 'presence-status');
+
+ // Must send the presence?
+ if((show != old_show) || (status != old_status)) {
+ // Update the local stored status
+ setDB('options', 'presence-status', status);
+
+ // Update the server stored status
+ if(status != old_status)
+ storeOptions();
+
+ // Send the presence
+ presenceSend();
+ }
+ })
+
+ // Input focus handler
+ .focus(function() {
+ closeBubbles();
+ });
+}