/* Jappix - An open social platform These are the roster JS scripts for Jappix ------------------------------------------------- License: AGPL Author: Valérian Saliou Last revision: 08/08/12 */ // Gets the roster items function getRoster() { var iq = new JSJaCIQ(); iq.setType('get'); iq.setQuery(NS_ROSTER); con.send(iq, handleRoster); } // Handles the roster items function handleRoster(iq) { // Parse the roster xml $(iq.getQuery()).find('item').each(function() { // Get user data var self = $(this); var user_xid = self.attr('jid'); var user_subscription = self.attr('subscription'); // Parse roster data & display user parseRoster($(this), 'load'); // Request user microblog (populates channel) if(user_xid && ((user_subscription == 'both') || (user_subscription == 'to'))) requestMicroblog(user_xid, 1, null, handleRosterMicroblog); }); // Update our avatar (if changed), and send our presence getAvatar(getXID(), 'force', 'true', 'forget'); logThis('Roster received.'); } // Parses the group XML and display the roster function parseRoster(current, mode) { // Get the values xid = current.attr('jid'); dName = current.attr('name'); subscription = current.attr('subscription'); xidHash = hex_md5(xid); // Create an array containing the groups var groups = new Array(); current.find('group').each(function() { var group_text = $(this).text(); if(group_text) groups.push(group_text); }); // No group? if(!groups.length) groups.push(_e("Unclassified")); // If no name is defined, we get the default nick of the buddy if(!dName) dName = getXIDNick(xid); displayRoster(xid, xidHash, dName, subscription, groups, mode); } // Updates the roster groups function updateGroups() { $('#buddy-list .one-group').each(function() { // Current values var check = $(this).find('.buddy').size(); var hidden = $(this).find('.buddy:not(.hidden-buddy:hidden)').size(); // Special case: the filtering tool if(SEARCH_FILTERED) hidden = $(this).find('.buddy:visible').size(); // If the group is empty if(!check) $(this).remove(); // If the group contains no online buddy (and is not just hidden) if(!hidden && $(this).find('a.group').hasClass('minus')) $(this).hide(); else $(this).show(); }); } // Displays a defined roster item function displayRoster(dXID, dXIDHash, dName, dSubscription, dGroup, dMode) { // First remove the buddy $('#buddy-list .' + dXIDHash).remove(); // Define some things around the groups var is_gateway = isGateway(dXID); var gateway = ''; if(is_gateway) { gateway = ' gateway'; dGroup = new Array(_e("Gateways")); } // Remove request if(dSubscription == 'remove') { // Flush presence flushPresence(dXID); presenceFunnel(dXID, dXIDHash); // Empty social channel $('#channel .mixed .one-update.update_' + dXIDHash).remove(); } // Other request else { // Is this buddy blocked? var privacy_class = ''; var privacy_state = statusPrivacy('block', dXID); if(privacy_state == 'deny') privacy_class = ' blocked'; // For each group this buddy has for(i in dGroup) { var cGroup = dGroup[i]; if(cGroup) { // Process some vars var groupHash = 'group' + hex_md5(cGroup); var groupContent = '#buddy-list .' + groupHash; var groupBuddies = groupContent + ' .group-buddies'; // Is this group blocked? if((statusPrivacy('block', cGroup) == 'deny') && (privacy_state != 'allow')) privacy_class = ' blocked'; // Group not yet displayed if(!exists(groupContent)) { // Define some things var groupCont = '#buddy-list .content'; var groupToggle = groupCont + ' .' + groupHash + ' a.group'; // Create the HTML markup of the group $(groupCont).prepend( '
' + '' + cGroup.htmlEnc() + '' + '
' + '
' ); // Create the click event which will hide and show the content $(groupToggle).click(function() { var group = $(groupBuddies); var group_toggle = $(groupContent + ' a.group'); // We must hide the buddies if(group_toggle.hasClass('minus')) { group.hide(); group_toggle.removeClass('minus').addClass('plus'); // Remove the group opened buddy-info closeBubbles(); } // We must show the buddies else { group_toggle.removeClass('plus').addClass('minus'); group.show(); } return false; }); } // Initialize the HTML code var name_code = '

' + dName.htmlEnc() + '

'; var presence_code = '

' + _e("Unavailable") + '

'; var html = '
' + '
'; // Display avatar if not gateway if(!is_gateway) html += '
' + '' + '
'; html += '
'; // Special gateway code if(is_gateway) html += presence_code + name_code; else html += name_code + presence_code; html += '
'; // Create the DOM element for this buddy $(groupBuddies).append(html); // Apply the hover event applyBuddyHover(dXID, dXIDHash, dName, dSubscription, dGroup, groupHash); } } // Click event on this buddy $('#buddy-list .' + dXIDHash + ' .buddy-click').click(function() { return checkChatCreate(dXID, 'chat'); }); // We get the user presence if necessary if(dMode == 'presence') presenceFunnel(dXID, dXIDHash); // If the buddy must be shown if(BLIST_ALL) $('#buddy-list .' + dXIDHash).show(); } // We update our groups if(!SEARCH_FILTERED) updateGroups(); else funnelFilterBuddySearch(); } // Applies the buddy editing input events function applyBuddyInput(xid) { // Initialize var path = '#buddy-list .buddy[data-xid="' + escape(xid) + '"]'; var rename = path + ' .bm-rename input'; var group = path + ' .bm-group input'; var manage_infos = path + ' .manage-infos'; var bm_choose = manage_infos + ' div.bm-choose'; // Keyup events $(rename).keyup(function(e) { if(e.keyCode == 13) { // Send the item sendRoster(xid, '', trim($(rename).val()), thisBuddyGroups(xid)); // Remove the buddy editor closeBubbles(); return false; } }); $(group).keyup(function(e) { if(e.keyCode == 13) { // Empty input? if(!trim($(this).val())) { // Send the item sendRoster(xid, '', trim($(rename).val()), thisBuddyGroups(xid)); // Remove the buddy editor closeBubbles(); return false; } // Get the values var this_value = trim($(this).val()); var escaped_value = escape(this_value); // Check if the group yet exists var group_exists = false; $(bm_choose + ' label span').each(function() { if($(this).text() == this_value) group_exists = true; }); // Create a new checked checkbox if(!group_exists) $(bm_choose).prepend(''); // Check the checkbox $(bm_choose + ' input[data-group="' + escaped_value + '"]').attr('checked', true); // Reset the value of this input $(this).val(''); return false; } }); // Click events $(manage_infos + ' p.bm-authorize a.to').click(function() { closeBubbles(); sendSubscribe(xid, 'subscribed'); return false; }); $(manage_infos + ' p.bm-authorize a.from').click(function() { closeBubbles(); sendSubscribe(xid, 'subscribe'); return false; }); $(manage_infos + ' p.bm-authorize a.unblock').click(function() { closeBubbles(); // Update privacy settings pushPrivacy('block', ['jid'], [xid], ['allow'], [false], [true], [true], [true], '', 'roster'); $(path).removeClass('blocked'); // Enable the "block" list changePrivacy('block', 'active'); changePrivacy('block', 'default'); // Send an available presence sendPresence(xid, 'available', getUserShow(), getUserStatus()); return false; }); $(manage_infos + ' p.bm-remove a.remove').click(function() { closeBubbles(); // First unregister if gateway if(isGateway(xid)) unregisterGatewayRoster(xid); // Then send roster removal query sendRoster(xid, 'remove'); return false; }); $(manage_infos + ' p.bm-remove a.prohibit').click(function() { closeBubbles(); sendSubscribe(xid, 'unsubscribed'); return false; }); $(manage_infos + ' p.bm-remove a.block').click(function() { closeBubbles(); // Update privacy settings pushPrivacy('block', ['jid'], [xid], ['deny'], [false], [true], [true], [true], '', 'roster'); $(path).addClass('blocked'); // Enable the "block" list changePrivacy('block', 'active'); changePrivacy('block', 'default'); // Send an unavailable presence sendPresence(xid, 'unavailable'); // Remove the user presence for(var i = 0; i < storageDB.length; i++) { // Get the pointer values var current = storageDB.key(i); // If the pointer is on a stored presence if((explodeThis('_', current, 0) == 'presence') && (bareXID(explodeThis('_', current, 1)) == xid)) storageDB.removeItem(current); } // Manage his new presence presenceFunnel(xid, hex_md5(xid)); return false; }); $(manage_infos + ' a.save').click(function() { // Send the item sendRoster(xid, '', trim($(rename).val()), thisBuddyGroups(xid)); // Remove the buddy editor closeBubbles(); return false; }); } // Applies the buddy editing hover events function applyBuddyHover(xid, hash, nick, subscription, groups, group_hash) { // Generate the values var bPath = '#buddy-list .' + group_hash + ' .buddy[data-xid="' + escape(xid) + '"]'; var iPath = bPath + ' .buddy-infos'; // Apply the hover event $(bPath).hover(function() { // Another bubble exist if(exists('#buddy-list .buddy-infos')) return false; $(bPath).oneTime(200, function() { // Another bubble exist if(exists('#buddy-list .buddy-infos')) return false; // Add this bubble! showBubble(iPath); // Create the buddy infos DOM element $(bPath).append( '
' + '
' + '
' + '
' + '

' + _e("unknown") + '

' + '

' + _e("unknown") + '

' + '

' + _e("unknown") + '

' + '

' + _e("unknown") + '

' + '

' + _e("unknown") + '

' + '

' + _e("Profile") + ' / ' + _e("Channel") + ' / ' + _e("Commands") + '

' + '

' + _e("Edit") + '

' + '
' + '
' + '
' ); // Sets the good position buddyInfosPosition(xid, group_hash); // Get the presence presenceFunnel(xid, hash); // Get the PEP infos displayAllPEP(xid); // Click events $(bPath + ' .bi-view a').click(function() { // Renitialize the buddy infos closeBubbles(); // Profile if($(this).is('.profile')) openUserInfos(xid); // Channel else if($(this).is('.channel')) fromInfosMicroblog(xid, hash); // Command else if($(this).is('.commands')) retrieveAdHoc(xid); return false; }); $(bPath + ' .bi-edit a').click(function() { buddyEdit(xid, nick, subscription, groups); return false; }); }); }, function() { if(!exists(iPath + ' .manage-infos')) closeBubbles(); $(bPath).stopTime(); }); } // Sets the good buddy-infos position function buddyInfosPosition(xid, group_hash) { // Paths var group = '#buddy-list .' + group_hash; var buddy = group + ' .buddy[data-xid="' + escape(xid) + '"]'; var buddy_infos = buddy + ' .buddy-infos'; // Get the offset to define var offset = 3; if(isGateway(xid)) offset = -8; // Process the position var v_position = $(buddy).position().top + offset; var h_position = $(buddy).width() - 10; // Apply the top position $(buddy_infos).css('top', v_position); // Apply the left/right position if($('html').attr('dir') == 'rtl') $(buddy_infos).css('right', h_position); else $(buddy_infos).css('left', h_position); } // Generates an array of the current groups of a buddy function thisBuddyGroups(xid) { var path = '#buddy-list .buddy[data-xid="' + escape(xid) + '"] '; var array = new Array(); // Each checked checkboxes $(path + 'div.bm-choose input[type="checkbox"]').filter(':checked').each(function() { array.push(unescape($(this).attr('data-group'))); }); // Entered input value (and not yet in the array) var value = trim($(path + 'p.bm-group input').val()); if(value && !existArrayValue(array, value)) array.push(value); return array; } // Adds a given contact to our roster function addThisContact(xid, name) { logThis('Add this contact: ' + xid + ', as ' + name, 3); // Cut the resource of this XID xid = bareXID(xid); // If the form is complete if(xid) { // We send the subscription sendSubscribe(xid, 'subscribe'); sendRoster(xid, '', name); // We hide the bubble closeBubbles(); } } // Gets an array of all the groups in the roster function getAllGroups() { var groups = new Array(); $('#buddy-list .one-group').each(function() { var current = unescape($(this).attr('data-group')); if((current != _e("Unclassified")) && (current != _e("Gateways"))) groups.push(current); }); return groups.sort(); } // Edits buddy informations function buddyEdit(xid, nick, subscription, groups) { logThis('Buddy edit: ' + xid, 3); // Initialize var path = '#buddy-list .buddy[data-xid="' + escape(xid) + '"] .'; var html = '
'; // Get the privacy state var privacy_state = statusPrivacy('block', xid); var privacy_active = getDB('privacy-marker', 'available'); // Get the group privacy state for(g in groups) { if((statusPrivacy('block', groups[g]) == 'deny') && (privacy_state != 'allow')) privacy_state = 'deny'; } // The subscription with this buddy is not full if((subscription != 'both') || ((privacy_state == 'deny') && privacy_active)) { var authorize_links = ''; html += '

'; // Link to allow to see our status if((subscription == 'to') || (subscription == 'none')) authorize_links += '' + _e("Authorize") + ''; // Link to ask to see his/her status if((subscription == 'from') || (subscription == 'none')) { if(authorize_links) authorize_links += ' / '; authorize_links += '' + _e("Ask for authorization") + ''; } // Link to unblock this buddy if((privacy_state == 'deny') && privacy_active) { if(authorize_links) authorize_links += ' / '; html += '' + _e("Unblock") + ''; } html += authorize_links + '

'; } // Complete the HTML code var remove_links = ''; html += '

'; remove_links = '' + _e("Remove") + ''; // This buddy is allowed to see our presence, we can show a "prohibit" link if((subscription == 'both') || (subscription == 'from')) remove_links += ' / ' + _e("Prohibit") + ''; // Complete the HTML code if((privacy_state != 'deny') && privacy_active) { if(remove_links) remove_links += ' / '; remove_links += '' + _e("Block") + ''; } // Complete the HTML code html += remove_links + '

' + '

'; // Only show group tool if not a gateway if(!isGateway(xid)) html += '

' + '
' + '
' + '
'; // Close the DOM element html += '' + _e("Save") + '' + '
'; // We update the DOM elements $(path + 'pep-infos').replaceWith(html); // Gets all the existing groups var all_groups = getAllGroups(); var all_groups_dom = ''; for(a in all_groups) { // Current group var all_groups_current = all_groups[a]; // Is the current group checked? var checked = ''; if(existArrayValue(groups, all_groups_current)) checked = ' checked="true"'; // Add the current group HTML all_groups_dom += ''; } // Prepend this in the DOM var bm_choose = path + 'manage-infos div.bm-choose'; $(bm_choose).prepend(all_groups_dom); // Apply the editing input events applyBuddyInput(xid); } // Unregisters from a given gateway function unregisterGatewayRoster(xid) { var iq = new JSJaCIQ(); iq.setType('set'); iq.setTo(xid); var query = iq.setQuery(NS_REGISTER); query.appendChild(iq.buildNode('remove', {'xmlns': NS_REGISTER})); con.send(iq); } // Updates the roster items function sendRoster(xid, subscription, name, group) { // We send the new buddy name var iq = new JSJaCIQ(); iq.setType('set'); var iqQuery = iq.setQuery(NS_ROSTER); var item = iqQuery.appendChild(iq.buildNode('item', {'xmlns': NS_ROSTER, 'jid': xid})); // Any subscription? if(subscription) item.setAttribute('subscription', subscription); // Any name? if(name) item.setAttribute('name', name); // Any group? if(group && group.length) { for(i in group) item.appendChild(iq.buildNode('group', {'xmlns': NS_ROSTER}, group[i])); } con.send(iq); logThis('Roster item sent: ' + xid, 3); } // Adapts the roster height, depending of the window size function adaptRoster() { // Process the new height var new_height = $('#left-content').height() - $('#my-infos').height() - 97; // New height too small if(new_height < 211) new_height = 211; // Apply the new height $('#buddy-list .content').css('height', new_height); } // Gets all the buddies in our roster function getAllBuddies() { var buddies = new Array(); $('#buddy-list .buddy').each(function() { var xid = unescape($(this).attr('data-xid')); if(xid) buddies.push(xid); }); return buddies; } // Gets the user gateways function getGateways() { // New array var gateways = new Array(); var buddies = getAllBuddies(); // Get the gateways for(c in buddies) { if(isGateway(buddies[c])) gateways.push(buddies[c]); } return gateways; } // Define a global var for buddy list all buddies displayed var BLIST_ALL = false; // Plugin launcher function launchRoster() { // Filtering tool var iFilter = $('#buddy-list .filter input'); var aFilter = $('#buddy-list .filter a'); iFilter.placeholder() .blur(function() { // Nothing is entered, put the placeholder instead if(!trim($(this).val())) aFilter.hide(); else aFilter.show(); }) .keyup(function(e) { funnelFilterBuddySearch(e.keyCode); }); aFilter.click(function() { // Reset the input $(this).hide(); iFilter.val(''); iFilter.placeholder(); // Security: show all the groups, empty or not $('#buddy-list .one-group').show(); // Reset the filtering tool resetFilterBuddySearch(); return false; }); // When the user click on the add button, show the contact adding tool $('#buddy-list .foot .add').click(function() { // Yet displayed? if(exists('#buddy-conf-add')) return closeBubbles(); // Add the bubble showBubble('#buddy-conf-add'); // Append the content $('#buddy-list .buddy-list-add').append( '
' + '
' + '
' + '

' + _e("Add a friend") + '

' + '' + '' + '' + '' + _e("Getting the name...") + '' + '

' + '' + _e("Search a friend") + '' + '

' + '
' + '
' ); // Add the gateways var gateways = getGateways(); // Any gateway? if(gateways.length) { // Append the gateways for(i in gateways) $('.add-contact-gateway').append(''); // Show the gateway selector $('.add-contact-gateway').parent().show(); } // No gateway? else $('.add-contact-gateway').parent().hide(); // Blur event on the add contact input $('.add-contact-jid').blur(function() { // Read the value var value = trim($(this).val()); // Try to catch the buddy name if(value && !trim($('.add-contact-name').val()) && ($('.add-contact-gateway').val() == 'none')) { // User XID var xid = generateXID(value, 'chat'); // Notice for the user $('.add-contact-name-get').attr('data-for', escape(xid)).show(); // Request the user vCard getAddUserName(xid); } }); // When a key is pressed... $('#buddy-conf-add input, #buddy-conf-add select').keyup(function(e) { // Enter : continue if(e.keyCode == 13) { // Get the values var xid = trim($('.add-contact-jid').val()); var name = trim($('.add-contact-name').val()); var gateway = unescape($('.add-contact-gateway').val()); // Generate the XID to add if((gateway != 'none') && xid) xid = xid.replace(/@/g, '%') + '@' + gateway; else xid = generateXID(xid, 'chat'); // Submit the form if(xid && getXIDNick(xid) && (xid != getXID())) addThisContact(xid, name); else $(document).oneTime(10, function() { $('.add-contact-jid').addClass('please-complete').focus(); }); return false; } // Escape : quit if(e.keyCode == 27) closeBubbles(); }); // Click event on search link $('.buddy-conf-add-search').click(function() { closeBubbles(); return openDirectory(); }); // Focus on the input $(document).oneTime(10, function() { $('.add-contact-jid').focus(); }); return false; }); // When the user click on the join button, show the chat joining tool $('#buddy-list .foot .join').click(function() { // Yet displayed? if(exists('#buddy-conf-join')) return closeBubbles(); // Add the bubble showBubble('#buddy-conf-join'); // Append the content $('#buddy-list .buddy-list-join').append( '
' + '
' + '' + '
' ); // Input vars var destination = '#buddy-conf-join .search'; var dHovered = destination + ' ul li.hovered:first'; // When a key is pressed... $('#buddy-conf-join input, #buddy-conf-join select').keyup(function(e) { // Enter: continue if(e.keyCode == 13) { // Select something from the search if(exists(dHovered)) addBuddySearch(destination, $(dHovered).attr('data-xid')); // Join something else { var xid = trim($('.join-jid').val()); var type = $('.buddy-conf-join-select').val(); if(xid && type) { // Generate a correct XID xid = generateXID(xid, type); // Not me if(xid != getXID()) { // Update some things $('.join-jid').removeClass('please-complete'); closeBubbles(); // Create a new chat checkChatCreate(xid, type); } else $('.join-jid').addClass('please-complete'); } else $('.join-jid').addClass('please-complete'); } return false; } // Escape: quit else if(e.keyCode == 27) closeBubbles(); // Buddy search? else if($('.buddy-conf-join-select').val() == 'chat') { // New buddy search if((e.keyCode != 40) && (e.keyCode != 38)) createBuddySearch(destination); // Navigating with keyboard in the results arrowsBuddySearch(e, destination); } }); // Buddy search lost focus $('#buddy-conf-join input').blur(function() { if(!$(destination + ' ul').attr('mouse-hover')) resetBuddySearch(destination); }); // Re-focus on the text input $('#buddy-conf-join select').change(function() { $(document).oneTime(10, function() { $('#buddy-conf-join input').focus(); }); }); // We focus on the input $(document).oneTime(10, function() { $('#buddy-conf-join .join-jid').focus(); }); return false; }); // When the user click on the groupchat button, show the groupchat menu $('#buddy-list .foot .groupchat').click(function() { // Yet displayed? if(exists('#buddy-conf-groupchat')) return closeBubbles(); // Add the bubble showBubble('#buddy-conf-groupchat'); // Append the content $('#buddy-list .buddy-list-groupchat').append( '
' + '
' + '
' + '

' + _e("Your groupchats") + '

' + '' + '

' + '- ' + _e("Manage your favorite groupchats") + '' + '

' + '
' + '
' ); // When the user wants to edit his groupchat favorites $('.buddy-conf-groupchat-edit').click(function() { openFavorites(); closeBubbles(); return false; }); // Change event $('.buddy-conf-groupchat-select').change(function() { var groupchat = trim($(this).val()); if(groupchat != 'none') { // We hide the bubble closeBubbles(); // Create the chat checkChatCreate(groupchat, 'groupchat'); // We reset the select value $(this).val('none'); } }); // Load the favorites loadFavorites(); return false; }); // When the user click on the more button, show the more menu $('#buddy-list .foot .more').click(function() { // Yet displayed? if(exists('#buddy-conf-more')) return closeBubbles(); // Add the bubble showBubble('#buddy-conf-more'); // Append the content $('#buddy-list .buddy-list-more').append( '
' + '
' + '
' + '

' + _e("More stuff") + '

' + '

' + '- ' + _e("Show all friends") + '' + '' + _e("Only show connected friends") + '' + '

' + '

' + '- ' + _e("Privacy") + '' + '

' + '

' + '- ' + _e("Service discovery") + '' + '

' + '

' + '- ' + _e("Commands") + '' + '

' + '
' + '
' ); // Close bubble when link clicked $('#buddy-conf-more a').click(function() { closeBubbles(); }); // When the user wants to display all his buddies $('.buddy-conf-more-display-unavailable').click(function() { showAllBuddies('roster'); return false; }); // When the user wants to display only online buddies $('.buddy-conf-more-display-available').click(function() { showOnlineBuddies('roster'); return false; }); // When the user click on the privacy link $('.buddy-conf-more-privacy').click(openPrivacy); // When the user click on the service discovery link $('.buddy-conf-more-service-disco').click(openDiscovery); // When the user click on the command link $('.buddy-conf-more-commands').click(function() { serverAdHoc(con.domain); return false; }); // Manage the displayed links if(BLIST_ALL) { $('.buddy-conf-more-display-unavailable').hide(); $('.buddy-conf-more-display-available').show(); } if(enabledCommands()) $('.buddy-conf-more-commands').parent().show(); if(getDB('privacy-marker', 'available')) $('.buddy-conf-more-privacy').parent().show(); return false; }); // When the user scrolls the buddy list $('#buddy-list .content').scroll(function() { // Close the opened buddy infos bubble closeBubbles(); }); } // Window resize event handler $(window).resize(adaptRoster);