/* Jappix - An open social platform These are the presence JS scripts for Jappix ------------------------------------------------- License: AGPL Author: Valérian Saliou */ // Bundle var Presence = (function () { /** * Alias of this * @private */ var self = {}; /* Variables */ self.first_sent = false; self.auto_idle = false; /** * Sends the user first presence * @public * @param {string} checksum * @return {undefined} */ self.sendFirst = function(checksum) { try { Console.info('First presence sent.'); // Jappix is now ready: change the title Interface.title('talk'); // Anonymous check var is_anonymous = Utils.isAnonymous(); // Update our marker self.first_sent = true; // Try to use the last status message var status = DataStore.getDB(Connection.desktop_hash, 'options', 'presence-status'); if(!status) status = ''; // We tell the world that we are online if(!is_anonymous) self.send('', '', '', 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 DateUtils.presence_last_activity = DateUtils.getTimeStamp(); // We store our presence DataStore.setDB(Connection.desktop_hash, '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) Storage.get(NS_BOOKMARKS); // We open a new chat if a XMPP link was submitted if((parent.location.hash != '#OK') && XMPPLinks.links_var.x) { // A link is submitted in the URL XMPPLinks.go(XMPPLinks.links_var.x); // Set a OK status parent.location.hash = 'OK'; } } } catch(e) { Console.error('Presence.sendFirst', e); } }; /** * Handles incoming presence packets * @public * @param {object} presence * @return {undefined} */ self.handle = function(presence) { try { // We define everything needed here var from = Common.fullXID(Common.getStanzaFrom(presence)); var hash = hex_md5(from); var node = presence.getNode(); var xid = Common.bareXID(from); var xidHash = hex_md5(xid); var resource = Common.thisResource(from); var resources_obj, xml; // 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 == Common.getXID()) && (hasPhoto == 'true') && (checksum != DataStore.getDB(Connection.desktop_hash, 'checksum', 1))) Avatar.get(Common.getXID(), 'force', 'true', 'forget'); // This presence comes from a groupchat if(Utils.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 = resource; var messageTime = DateUtils.getCompleteTime(); var notInitial = true; // Read the status code var status_code = []; 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(Common.exists('#' + xidHash + '[data-initial="true"]')) { notInitial = false; } // If one user is quitting if(type && (type == 'unavailable')) { self.displayMUC(from, xidHash, hash, type, show, status, affiliation, role, reason, status_code, iXID, iNick, messageTime, nick, notInitial); DataStore.removeDB(Connection.desktop_hash, 'presence-stanza', from); resources_obj = self.removeResource(xid, resource); } // If one user is joining else { // Fixes M-Link first presence bug (missing ID!) if((nick == Name.getMUCNick(xidHash)) && (presence.getID() === null) && !Common.exists('#page-engine #' + xidHash + ' .list .' + hash)) { Groupchat.handleMUC(presence); Console.warn('Passed M-Link MUC first presence handling.'); } else { self.displayMUC(from, xidHash, hash, type, show, status, affiliation, role, reason, status_code, iXID, iNick, messageTime, nick, notInitial); xml = '' + priority.htmlEnc() + '' + show.htmlEnc() + '' + type.htmlEnc() + '' + status.htmlEnc() + '' + hasPhoto.htmlEnc() + '' + checksum.htmlEnc() + '' + caps.htmlEnc() + ''; DataStore.setDB(Connection.desktop_hash, 'presence-stanza', from, xml); resources_obj = self.addResource(xid, resource); } } // Manage the presence self.processPriority(from, resource, resources_obj); self.funnel(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(Common.exists('#roster .buddy[data-xid="' + escape(xid) + '"]')) self.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 Notification.create('subscribe', xid, [xid, nickname], status); } } // Unsubscribe stanza else if(type == 'unsubscribe') { Roster.send(xid, 'remove'); } // Other stanzas else { // Unavailable/error presence if(type == 'unavailable') { DataStore.removeDB(Connection.desktop_hash, 'presence-stanza', from); resources_obj = self.removeResource(xid, resource); } // Other presence (available, subscribe...) else { xml = '' + priority.htmlEnc() + '' + show.htmlEnc() + '' + type.htmlEnc() + '' + status.htmlEnc() + '' + hasPhoto.htmlEnc() + '' + checksum.htmlEnc() + '' + caps.htmlEnc() + ''; DataStore.setDB(Connection.desktop_hash, 'presence-stanza', from, xml); resources_obj = self.addResource(xid, resource); } // We manage the presence self.processPriority(xid, resource, resources_obj); self.funnel(xid, xidHash); // We display the presence in the current chat if(Common.exists('#' + xidHash)) { var dStatus = self.filterStatus(xid, status, false); if(dStatus) dStatus = ' (' + dStatus + ')'; // Generate the presence-in-chat code var dName = Name.getBuddy(from).htmlEnc(); var dBody = dName + ' (' + from + ') ' + Common._e("is now") + ' ' + self.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) Message.display('chat', xid, xidHash, dName, dBody, DateUtils.getCompleteTime(), DateUtils.getTimeStamp(), 'system-message', false); } } // Get disco#infos for this presence (related to Caps) Caps.getDiscoInfos(from, caps); } // For logger if(!show) { if(!type) show = 'available'; else show = 'unavailable'; } Console.log('Presence received: ' + show + ', from ' + from); } catch(e) { Console.error('Presence.handle', e); } }; /** * Displays a MUC presence * @public * @param {string} from * @param {string} roomHash * @param {string} hash * @param {string} type * @param {string} show * @param {string} status * @param {string} affiliation * @param {string} role * @param {string} reason * @param {string} status_code * @param {string} iXID * @param {string} iNick * @param {string} messageTime * @param {string} nick * @param {boolean} initial * @return {undefined} */ self.displayMUC = function(from, roomHash, hash, type, show, status, affiliation, role, reason, status_code, iXID, iNick, messageTime, nick, initial) { try { // Generate the values var room_xid = Common.bareXID(from); 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(Common.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 = Common.bareXID(iXID); write += ' (' + iXID + ') '; } // User does not exists yet if(!Common.exists(thisUser) && (!type || (type == 'available'))) { var myself = ''; // Is it me? if(nick == Name.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( '
' + '
' + '
' + nick_html + '
' + '
' + '' + '
' + '
' + '
' + '
' + '' + '' + '' + '' + '' + '' + '' + '' + '' + '' + '' + '' + '
' + '
' + '
' ); // Click event if(nick != Name.getMUCNick(roomHash)) { $(thisUser).hover(function() { if(iXID && Groupchat.affiliationMe(room_xid).code >= 2) { var user_actions_sel = $(this).find('.user-actions'); var user_actions_btn_sel = user_actions_sel.find('.action'); // Update buttons var i; var hide_btns = []; var user_affiliation = Groupchat.affiliationUser(room_xid, nick); if(user_affiliation.name == 'owner') { hide_btns.push('promote'); hide_btns.push('demote'); hide_btns.push('kick'); } else if(user_affiliation.name === 'admin') { hide_btns.push('promote'); hide_btns.push('kick'); } else { hide_btns.push('demote'); } if(Roster.isFriend(iXID)) { hide_btns.push('add'); } // Go Go Go!! for(i in hide_btns) { user_actions_btn_sel.filter('.' + hide_btns[i]).hide(); } // Slide down? if(hide_btns.length < user_actions_btn_sel.size()) { user_actions_sel.stop(true).slideDown(250); } } }, function() { var user_actions_sel = $(this).find('.user-actions'); if(user_actions_sel.is(':visible')) { user_actions_sel.stop(true).slideUp(200, function() { user_actions_sel.find('.action').show(); }); } }); $(thisUser).find('.user-details').on('click', function() { Chat.checkCreate(from, 'private'); }); $(thisUser).find('.user-actions .action a').on('click', function() { var this_parent_sel = $(this).parent(); if(this_parent_sel.is('.promote')) { Groupchat.promoteModerator(room_xid, iXID); } else if(this_parent_sel.is('.demote')) { Groupchat.demoteModerator(room_xid, iXID); } else if(this_parent_sel.is('.add')) { this_parent_sel.hide(); Roster.addThisContact(iXID, nick); } else if(this_parent_sel.is('.kick')) { Groupchat.kickUser(room_xid, (iXID || from), nick); } return false; }); } // We tell the user that someone entered the room if(!initial && DataStore.getDB(Connection.desktop_hash, 'options', 'groupchatpresence') !== '0') { notify = true; write += Common._e("joined the chat room"); // Any status? if(status) write += ' (' + Filter.message(status, nick_html, true) + ')'; else write += ' (' + Common._e("no status") + ')'; } // Enable the private chat input thisPrivate.removeAttr('disabled'); } else if((type == 'unavailable') || (type == 'error')) { // Is it me? if(nick == Name.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(Utils.existArrayValue(status_code, 301) || Utils.existArrayValue(status_code, 307)) { $(thisUser).remove(); notify = true; // Kicked? if(Utils.existArrayValue(status_code, 307)) write += Common._e("has been kicked"); // Banned? if(Utils.existArrayValue(status_code, 301)) write += Common._e("has been banned"); // Any reason? if(reason) write += ' (' + Filter.message(reason, nick_html, true) + ')'; else write += ' (' + Common._e("no reason") + ')'; } // Nickname change? else if(Utils.existArrayValue(status_code, 303) && iNick) { notify = true; write += Common.printf(Common._e("changed his/her nickname to %s"), iNick.htmlEnc()); // New values var new_xid = Common.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).off('click'); // Change to the new nickname $(thisUser).attr('data-nick', escape(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).on('click', function() { Chat.checkCreate(new_xid, 'private'); }); } // We tell the user that someone left the room else if(!initial && DataStore.getDB(Connection.desktop_hash, 'options', 'groupchatpresence') !== '0') { $(thisUser).remove(); notify = true; write += Common._e("left the chat room"); // Any status? if(status) write += ' (' + Filter.message(status, nick_html, true) + ')'; else write += ' (' + Common._e("no status") + ')'; } // Disable the private chat input thisPrivate.attr('disabled', true); } // Must notify something if(notify) Message.display('groupchat', from, roomHash, nick_html, write, messageTime, DateUtils.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(); }); } catch(e) { Console.error('Presence.displayMUC', e); } }; /** * Filters a given status * @public * @param {string} xid * @param {string} status * @param {boolean} cut * @return {string} */ self.filterStatus = function(xid, status, cut) { try { var dStatus = ''; if(!status) { status = ''; } else { if(cut) { dStatus = Utils.truncate(status, 50); } else { dStatus = status; } dStatus = Filter.message(dStatus, Name.getBuddy(xid).htmlEnc(), true); } return dStatus; } catch(e) { Console.error('Presence.filterStatus', e); } }; /** * Displays a user's presence * @public * @param {string} value * @param {string} type * @param {string} show * @param {string} status * @param {string} hash * @param {string} xid * @param {string} avatar * @param {string} checksum * @param {string} caps * @return {undefined} */ self.display = function(value, type, show, status, hash, xid, avatar, checksum, caps) { try { // Display the presence in the roster var path = '#roster .' + hash; var buddy = $('#roster .content .' + hash); var dStatus = self.filterStatus(xid, status, false); var tStatus = Common.encodeQuotes(status); var biStatus; // The buddy presence behind his name $(path + ' .name .buddy-presence').replaceWith('

' + value + '

'); // The buddy presence in the buddy infos if(dStatus) biStatus = dStatus; else biStatus = value; $(path + ' .bi-status').replaceWith('

' + biStatus + '

'); // 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.search_filtered) buddy.hide(); // All the buddies are shown? if(Roster.blist_all) buddy.show(); // Chat stuffs if(Common.exists('#' + hash)) { // Remove the chatstate stuffs ChatState.reset(hash); $('#' + hash + ' .chatstate').remove(); $('#' + hash + ' .message-area').removeAttr('data-chatstates'); // Get the buddy avatar (only if a chat is opened) Avatar.get(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.search_filtered) buddy.show(); // Get the online buddy avatar if not a gateway Avatar.get(xid, 'cache', avatar, checksum); } // Display the presence in the chat if(Common.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('

' + value + '' + dStatus + '

'); // Process the new status position self.adaptChat(hash); } // Display the presence in the switcher if(Common.exists('#page-switch .' + hash)) { $('#page-switch .' + hash + ' .icon').removeClass('available unavailable error away busy').addClass(type); } // Update roster groups if(!Search.search_filtered) { Roster.updateGroups(); } else { Search.funnelFilterBuddy(); } // Get the disco#infos for this user var highest = self.highestPriority(xid); if(highest) { Caps.getDiscoInfos(highest, caps); } else { Caps.displayDiscoInfos(xid, ''); } } catch(e) { Console.error('Presence.display', e); } }; /** * Process the chat presence position * @public * @param {string} hash * @return {undefined} */ self.adaptChat = function(hash) { try { // Get values var pep_numb = $('#' + hash + ' .bc-pep').find('a').size(); // Process the left/right position var presence_h = 12; if(pep_numb) presence_h = (pep_numb * 20) + 18; // Apply the left/right position var presence_h_tag = ($('html').attr('dir') == 'rtl') ? 'left' : 'right'; $('#' + hash + ' p.bc-infos').css(presence_h_tag, presence_h); } catch(e) { Console.error('Presence.adaptChat', e); } }; /** * Convert the presence "show" element into a human-readable output * @public * @param {string} show * @param {string} type * @return {undefined} */ self.humanShow = function(show, type) { try { if(type == 'unavailable') show = Common._e("Unavailable"); else if(type == 'error') show = Common._e("Error"); else { switch(show) { case 'chat': show = Common._e("Talkative"); break; case 'away': show = Common._e("Away"); break; case 'xa': show = Common._e("Not available"); break; case 'dnd': show = Common._e("Busy"); break; default: show = Common._e("Available"); break; } } return show; } catch(e) { Console.error('Presence.humanShow', e); } }; /** * Makes the presence data go in the right way * @public * @param {string} type * @param {string} show * @param {string} status * @param {string} hash * @param {string} xid * @param {string} avatar * @param {string} checksum * @param {string} caps * @return {undefined} */ self.IA = function(type, show, status, hash, xid, avatar, checksum, caps) { try { // Is there a status defined? if(!status) status = self.humanShow(show, type); // Then we can handle the events if(type == 'error') self.display(Common._e("Error"), 'error', show, status, hash, xid, avatar, checksum, caps); else if(type == 'unavailable') self.display(Common._e("Unavailable"), 'unavailable', show, status, hash, xid, avatar, checksum, caps); else { switch(show) { case 'chat': self.display(Common._e("Talkative"), 'available', show, status, hash, xid, avatar, checksum, caps); break; case 'away': self.display(Common._e("Away"), 'away', show, status, hash, xid, avatar, checksum, caps); break; case 'xa': self.display(Common._e("Not available"), 'busy', show, status, hash, xid, avatar, checksum, caps); break; case 'dnd': self.display(Common._e("Busy"), 'busy', show, status, hash, xid, avatar, checksum, caps); break; default: self.display(Common._e("Available"), 'available', show, status, hash, xid, avatar, checksum, caps); break; } } } catch(e) { Console.error('Presence.IA', e); } }; /** * Flush the presence data for a given user * @public * @param {string} xid * @return {boolean} */ self.flush = function(xid) { try { var flushed_marker = false; var db_regex = new RegExp(('^' + Connection.desktop_hash + '_') + 'presence' + ('_(.+)')); for(var i = 0; i < DataStore.storageDB.length; i++) { // Get the pointer values var current = DataStore.storageDB.key(i); // If the pointer is on a stored presence if(current.match(db_regex)) { // Get the current XID var now_full = RegExp.$1; var now_bare = Common.bareXID(now_full); // If the current XID equals the asked XID if(now_bare == xid) { if(DataStore.removeDB(Connection.desktop_hash, 'presence-stanza', now_full)) { Console.info('Presence data flushed for: ' + now_full); flushed_marker = true; i--; } } } } return flushed_marker; } catch(e) { Console.error('Presence.flush', e); } }; /** * Process the highest resource priority for an user * @public * @param {string} xid * @param {string} resource * @param {object} resources_obj * @return {undefined} */ self.processPriority = function(xid, resource, resources_obj) { try { if(!xid) { Console.warn('No XID value'); return; } // Initialize vars var cur_resource, cur_from, cur_pr, cur_xml, cur_priority, from_highest; from_highest = null; max_priority = null; // Groupchat presence? (no priority here) if(xid.indexOf('/') !== -1) { from_highest = xid; Console.log('Processed presence for groupchat user: ' + xid); } else { if(!self.highestPriority(xid)) { from_highest = xid + '/' + resource; Console.log('Processed initial presence for regular user: ' + xid + ' (highest priority for: ' + (from_highest || 'none') + ')'); } else { for(cur_resource in resources_obj) { // Read presence data cur_from = xid + '/' + cur_resource; cur_pr = DataStore.getDB(Connection.desktop_hash, 'presence-stanza', cur_from); if(cur_pr) { // Parse presence data cur_xml = Common.XMLFromString(cur_pr); cur_priority = $(cur_xml).find('priority').text(); cur_priority = !isNaN(cur_priority) ? parseInt(cur_priority) : 0; // Higher priority? if((cur_priority >= max_priority) || (max_priority === null)) { max_priority = cur_priority; from_highest = cur_from; } } } Console.log('Processed presence for regular user: ' + xid + ' (highest priority for: ' + (from_highest || 'none') + ')'); } } if(from_highest) DataStore.setDB(Connection.desktop_hash, 'presence-priority', xid, from_highest); else DataStore.removeDB(Connection.desktop_hash, 'presence-priority', xid); } catch(e) { Console.error('Presence.processPriority', e); } }; /** * Returns the highest presence priority XID for an user * @public * @param {string} xid * @return {string} */ self.highestPriority = function(xid) { try { return DataStore.getDB(Connection.desktop_hash, 'presence-priority', xid) || ''; } catch(e) { Console.error('Presence.highestPriority', e); } }; /** * Gets the presence stanza for given full XID * @public * @param {string} xid_full * @return {object} */ self.readStanza = function(xid_full) { try { var pr = DataStore.getDB(Connection.desktop_hash, 'presence-stanza', xid_full); if(!pr) { pr = 'unavailable'; } return Common.XMLFromString(pr); } catch(e) { Console.error('Presence.readStanza', e); } }; /** * Gets the resource from a XID which has the highest priority * @public * @param {string} xid * @return {object} */ self.highestPriorityStanza = function(xid) { try { return self.readStanza( self.highestPriority(xid) ); } catch(e) { Console.error('Presence.highestPriorityStanza', e); } }; /** * Lists presence resources for an user * @public * @param {string} xid * @return {object} */ self.resources = function(xid) { try { var resources_obj = {}; var resources_db = DataStore.getDB(Connection.desktop_hash, 'presence-resources', xid); if(resources_db) { resources_obj = $.evalJSON(resources_db); } return resources_obj; } catch(e) { Console.error('Presence.resources', e); } }; /** * Adds a given presence resource for an user * @public * @param {string} xid * @param {string} resource * @return {object} */ self.addResource = function(xid, resource) { var resources_obj = null; try { resources_obj = self.resources(xid); resources_obj[resource] = 1; DataStore.setDB(Connection.desktop_hash, 'presence-resources', xid, $.toJSON(resources_obj)); } catch(e) { Console.error('Presence.addResource', e); } finally { return resources_obj; } }; /** * Removes a given presence resource for an user * @public * @param {string} xid * @param {string} resource * @return {object} */ self.removeResource = function(xid, resource) { var resources_obj = null; try { resources_obj = self.resources(xid); delete resources_obj[resource]; DataStore.setDB(Connection.desktop_hash, 'presence-resources', xid, $.toJSON(resources_obj)); } catch(e) { Console.error('Presence.removeResource', e); } finally { return resources_obj; } }; /** * Makes something easy to process for the presence IA * @public * @param {string} xid * @param {string} hash * @return {undefined} */ self.funnel = function(xid, hash) { try { // Get the highest priority presence value var xml = $(self.highestPriorityStanza(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) self.IA('', 'available', status, hash, xid, avatar, checksum, caps); else self.IA(type, show, status, hash, xid, avatar, checksum, caps); } catch(e) { Console.error('Presence.funnel', e); } }; /** * Sends a defined presence packet * @public * @param {string} to * @param {string} type * @param {string} show * @param {string} status * @param {string} checksum * @param {number} limit_history * @param {string} password * @param {function} handle * @return {undefined} */ self.send = function(to, type, show, status, checksum, limit_history, password, handle) { try { // Get some stuffs var priority = DataStore.getDB(Connection.desktop_hash, 'priority', 1); if(!priority) priority = '1'; if(!checksum) checksum = DataStore.getDB(Connection.desktop_hash, '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': 'http://jappix.org/', 'ver': Caps.mine()}); // Nickname var nickname = Name.get(); if(nickname && !limit_history) 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': DateUtils.getPresenceLast() })); } // Else, set a new last activity stamp else DateUtils.presence_last_activity = DateUtils.getTimeStamp(); // Send the presence packet if(handle) con.send(presence, handle); else con.send(presence); if(!type) type = 'available'; Console.info('Presence sent: ' + type); } catch(e) { Console.error('Presence.send', e); } }; /** * Performs all the actions to get the presence data * @public * @param {string} checksum * @param {boolean} autoidle * @return {undefined} */ self.sendActions = function(checksum, autoidle) { try { // We get the values of the inputs var show = self.getUserShow(); var status = self.getUserStatus(); // Send the presence if(!Utils.isAnonymous()) self.send('', '', show, status, checksum); // We set the good icon self.icon(show); // We store our presence if(!autoidle) DataStore.setDB(Connection.desktop_hash, '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) Groupchat.getMUC(room, nick); // Not disabled? else if(!$(this).find('.message-area').attr('disabled')) self.send(room + '/' + nick, '', show, status, '', true); }); } catch(e) { Console.error('Presence.quickSend', e); } }; /** * Changes the presence icon * @public * @param {string} value * @return {undefined} */ self.icon = function(value) { try { $('#my-infos .f-presence a.picker').attr('data-value', value); } catch(e) { Console.error('Presence.icon', e); } }; /** * Sends a subscribe stanza * @public * @param {string} to * @param {string} type * @return {undefined} */ self.sendSubscribe = function(to, type) { try { var status = ''; // Subscribe request? if(type == 'subscribe') status = Common.printf(Common._e("Hi, I am %s, I would like to add you as my friend."), Name.get()); self.send(to, type, '', status); } catch(e) { Console.error('Presence.sendSubscribe', e); } }; /** * Accepts the subscription from another entity * @public * @param {string} xid * @param {string} name * @return {undefined} */ self.acceptSubscribe = function(xid, name) { try { // We update our chat $('#' + hex_md5(xid) + ' .tools-add').hide(); // We send a subsribed presence (to confirm) self.sendSubscribe(xid, 'subscribed'); // We send a subscription request (subscribe both sides) self.sendSubscribe(xid, 'subscribe'); // Specify the buddy name (if any) if(name) { Roster.send(xid, '', name); } } catch(e) { Console.error('Presence.acceptSubscribe', e); } }; /** * Sends automatic away presence * @public * @return {undefined} */ self.autoIdle = function() { try { // Not connected? if(!Common.isConnected()) return; // Stop if an xa presence was set manually var last_presence = self.getUserShow(); if(!self.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(self.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(((!self.auto_idle && (last_presence != 'away')) || (self.auto_idle && (last_presence == 'away'))) && (DateUtils.getLastActivity() >= activity_limit)) { // Then tell we use an auto presence self.auto_idle = true; // Get the old status message var status = DataStore.getDB(Connection.desktop_hash, '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 self.sendActions('', true); Console.info('Auto-idle presence sent: ' + idle_presence); } } catch(e) { Console.error('Presence.autoIdle', e); } }; /** * Restores the old presence on a document bind * @public * @return {undefined} */ self.eventIdle = function() { try { // If we were idle, restore our old presence if(self.auto_idle) { // Get the values var show = DataStore.getDB(Connection.desktop_hash, 'presence-show', 1); var status = DataStore.getDB(Connection.desktop_hash, '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 self.sendActions('', true); if(!show) show = 'available'; Console.info('Presence restored: ' + show); } // Apply some values self.auto_idle = false; DateUtils.last_activity = DateUtils.getTimeStamp(); } catch(e) { Console.error('Presence.eventIdle', e); } }; /** * Lives the auto idle functions * @public * @return {undefined} */ self.liveIdle = function() { try { // Apply the autoIdle function every minute self.auto_idle = false; $('#my-infos .f-presence').everyTime('30s', self.autoIdle); // On body bind (click & key event) $('body').on('mousedown', self.eventIdle) .on('mousemove', self.eventIdle) .on('keydown', self.eventIdle); } catch(e) { Console.error('Presence.liveIdle', e); } }; /** * Kills the auto idle functions * @public * @return {undefined} */ self.dieIdle = function() { try { // Remove the event detector $('body').off('mousedown', self.eventIdle) .off('mousemove', self.eventIdle) .off('keydown', self.eventIdle); } catch(e) { Console.error('Presence.dieIdle', e); } }; /** * Gets the user presence show * @public * @return {string} */ self.getUserShow = function() { try { return $('#my-infos .f-presence a.picker').attr('data-value'); } catch(e) { Console.error('Presence.getUserShow', e); } }; /** * Gets the user presence status * @public * @return {string} */ self.getUserStatus = function() { try { return $('#presence-status').val(); } catch(e) { Console.error('Presence.getUserStatus', e); } }; /** * Plugin launcher * @public * @return {undefined} */ self.instance = function() { try { // 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 = [Common._e("Not available"), Common._e("Away"), Common._e("Available")]; var show_val = self.getUserShow(); // Yet displayed? var can_append = true; if(Common.exists(path)) can_append = false; // Add this bubble! Bubble.show(path); if(!can_append) return false; // Generate the HTML code var html = '
'; for(var i in show_id) { // Yet in use: no need to display it! if(show_id[i] == show_val) continue; html += ''; } html += '
'; // 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 Bubble.close(); // 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 = self.getUserShow(); var status = self.getUserStatus(); // Read the old parameters var old_show = DataStore.getDB(Connection.desktop_hash, 'presence-show', 1); var old_status = DataStore.getDB(Connection.desktop_hash, 'options', 'presence-status'); // Must send the presence? if((show != old_show) || (status != old_status)) { // Update the local stored status DataStore.setDB(Connection.desktop_hash, 'options', 'presence-status', status); // Update the server stored status if(status != old_status) Options.store(); // Send the presence self.sendActions(); } }) // Input focus handler .focus(function() { Bubble.close(); }); } catch(e) { Console.error('Presence.instance', e); } }; /** * Return class scope */ return self; })();