3 Jappix - An open social platform
4 These are the messages JS scripts for Jappix
6 -------------------------------------------------
9 Authors: Vanaryon, Maranda
10 Last revision: 01/09/11
14 // Handles the incoming message packets
15 function handleMessage(message) {
16 // Error packet? Stop!
17 if(handleErrorReply(message))
20 // We get the message items
21 var from = fullXID(getStanzaFrom(message));
22 var id = message.getID();
23 var type = message.getType();
24 var body = trim(message.getBody());
25 var node = message.getNode();
26 var subject = trim(message.getSubject());
28 // We generate some values
29 var xid = bareXID(from);
30 var resource = thisResource(from);
31 var hash = hex_md5(xid);
32 var xHTML = $(node).find('html body').size();
35 // This message comes from a groupchat user
36 if(isPrivate(xid) && ((type == 'chat') || !type) && resource) {
43 var time, stamp, d_stamp;
46 var delay = readMessageDelay(node);
50 time = relativeDate(delay);
51 d_stamp = Date.jab2date(delay);
54 // No delay: get actual time
56 time = getCompleteTime();
61 stamp = extractStamp(d_stamp);
64 if(hasReceived(message))
65 return messageReceived(hash, id);
68 if(node && !delay && ((((type == 'chat') || !type) && !exists('#page-switch .' + hash + ' .unavailable')) || (type == 'groupchat'))) {
69 /* REF: http://xmpp.org/extensions/xep-0085.html */
71 // Re-process the hash
72 var chatstate_hash = hash;
74 if(type == 'groupchat')
75 chatstate_hash = hex_md5(from);
77 // Do something depending of the received state
78 if($(node).find('active').size()) {
79 displayChatState('active', chatstate_hash, type);
81 // Tell Jappix the entity supports chatstates
82 $('#' + chatstate_hash + ' .message-area').attr('data-chatstates', 'true');
84 logThis('Active chatstate received from: ' + from);
87 else if($(node).find('composing').size())
88 displayChatState('composing', chatstate_hash, type);
90 else if($(node).find('paused').size())
91 displayChatState('paused', chatstate_hash, type);
93 else if($(node).find('inactive').size())
94 displayChatState('inactive', chatstate_hash, type);
96 else if($(node).find('gone').size())
97 displayChatState('gone', chatstate_hash, type);
101 if($(node).find('x[xmlns=' + NS_MUC_USER + '] invite').size()) {
102 // We get the needed values
103 var iFrom = $(node).find('x[xmlns=' + NS_MUC_USER + '] invite').attr('from');
104 var iRoom = $(node).find('x[xmlns=' + NS_XCONFERENCE + ']').attr('jid');
106 // Old invite method?
110 // We display the notification
111 newNotification('invite_room', iFrom, [iRoom], body);
113 logThis('Invite Request from: ' + iFrom + ' to join: ' + iRoom);
119 if(message.getChild('confirm', NS_HTTP_AUTH)) {
120 // Open a new notification
121 newNotification('request', xid, [message], body);
123 logThis('HTTP Request from: ' + xid);
129 if(message.getChild('x', NS_XOOB)) {
130 handleOOB(from, id, 'x', node);
132 logThis('Message OOB request from: ' + xid);
137 // Roster Item Exchange message
138 if(message.getChild('x', NS_ROSTERX)) {
139 // Open a new notification
140 newNotification('rosterx', xid, [message], body);
142 logThis('Roster Item Exchange from: ' + xid);
148 if((type == 'normal') && body) {
150 var messageDate = delay;
154 messageDate = getXMPPTime('utc');
157 var messageID = hex_md5(xid + subject + messageDate);
159 // We store the received message
160 storeInboxMessage(xid, subject, body, 'unread', messageID, messageDate);
162 // Display the inbox message
164 displayInboxMessage(xid, subject, body, 'unread', messageID, messageDate);
166 // Check we have new messages (play a sound if any unread messages)
167 if(checkInboxMessages())
170 // Send it to the server
177 if($(node).find('event').attr('xmlns') == NS_PUBSUB_EVENT) {
178 // We get the needed values
179 var iParse = $(node).find('event items');
180 var iNode = iParse.attr('node');
182 // Turn around the different result cases
187 // Retrieve the values
188 var iMood = iParse.find('mood');
193 if(iMood.children().size()) {
195 fValue = node.getElementsByTagName('mood').item(0).childNodes.item(0).nodeName;
198 tText = iMood.find('text').text();
205 // Store the PEP event (and display it)
206 storePEP(xid, 'mood', fValue, tText);
212 // Retrieve the values
213 var iActivity = iParse.find('activity');
218 if(iActivity.children().size()) {
220 fValue = node.getElementsByTagName('activity').item(0).childNodes.item(0).nodeName;
223 tText = iActivity.find('text').text();
230 // Store the PEP event (and display it)
231 storePEP(xid, 'activity', fValue, tText);
237 // Retrieve the values
238 var iTune = iParse.find('tune');
239 var tArtist = iTune.find('artist').text();
240 var tSource = iTune.find('source').text();
241 var tTitle = iTune.find('title').text();
242 var tURI = iTune.find('uri').text();
244 // Store the PEP event (and display it)
245 storePEP(xid, 'tune', tArtist, tTitle, tSource, tURI);
251 // Retrieve the values
252 var iGeoloc = iParse.find('geoloc');
253 var tLat = iGeoloc.find('lat').text();
254 var tLon = iGeoloc.find('lon').text();
257 var tLocality = iGeoloc.find('locality').text();
258 var tRegion = iGeoloc.find('region').text();
259 var tCountry = iGeoloc.find('country').text();
260 var tHuman = humanPosition(tLocality, tRegion, tCountry);
262 // Store the PEP event (and display it)
263 storePEP(xid, 'geoloc', tLat, tLon, tHuman);
269 displayMicroblog(message, xid, hash, 'mixed', 'push');
275 // Do not handle friend's notifications
277 handleNotifications(message);
286 // If this is a room topic message
287 if(subject && (type == 'groupchat')) {
289 var filter_subject = subject.replace(/\n+/g, ' ');
290 var filteredSubject = filterThisMessage(filter_subject, resource, true);
291 var filteredName = resource.htmlEnc();
293 // Display the new subject at the top
294 $('#' + hash + ' .top .name .bc-infos .muc-topic').replaceWith('<span class="muc-topic" title="' + filter_subject + '">' + filteredSubject + '</span>');
296 // Display the new subject as a system message
298 var topic_body = filteredName + ' ' + _e("changed the subject to:") + ' ' + filterThisMessage(subject, resource, true);
299 displayMessage(type, from, hash, filteredName, topic_body, time, stamp, 'system-message', false);
303 // If the message has a content
309 if((BrowserDetect.browser == 'Explorer') && (BrowserDetect.version < 9))
312 //If this is a xHTML message
316 // Filter the xHTML message
317 body = filterThisXHTML(node);
321 if(type == 'groupchat') {
322 /* REF: http://xmpp.org/extensions/xep-0045.html */
324 // We generate the message type and time
325 var message_type = 'user-message';
327 // This is an old message
328 if(delay && resource)
329 message_type = 'old-message';
331 // This is a system message
333 message_type = 'system-message';
337 // If this is not an old message
338 if(message_type == 'user-message') {
339 var myNick = getMUCNick(hash);
341 // If an user quoted our nick (with some checks)
342 var regex = new RegExp('((^)|( )|(@))' + escapeRegex(myNick) + '(($)|(:)|(,)|( ))', 'gi');
344 if(body.match(regex) && (myNick != resource) && (message_type == 'user-message'))
345 nickQuote = ' my-nick';
347 // We notify the user if there's a new personnal message
349 messageNotify(hash, 'personnal');
353 // We notify the user there's a new unread muc message
355 messageNotify(hash, 'unread');
358 // Display the received message
359 displayMessage(type, from, hash, resource.htmlEnc(), body, time, stamp, message_type, notXHTML, nickQuote);
363 else if((type == 'chat') || !type) {
364 // Gets the nickname of the user
365 var fromName = resource;
366 var chatType = 'chat';
368 // Must send a receipt notification?
369 if(hasReceipt(message) && (id != null))
370 sendReceived(type, from, id);
372 // It does not come from a groupchat user, get the full name
374 fromName = getBuddyName(xid);
376 chatType = 'private';
378 // If the chat isn't yet opened, open it !
379 if(!exists('#' + hash)) {
380 // We create a new chat
381 chatCreate(hash, xid, fromName, chatType);
383 // We tell the user that a new chat has started
390 // Display the received message
391 displayMessage(type, xid, hash, fromName.htmlEnc(), body, time, stamp, 'user-message', notXHTML, '', 'him');
393 // We notify the user
394 messageNotify(hash, 'personnal');
403 // Sends a given message
404 function sendMessage(hash, type) {
406 var message_area = $('#' + hash + ' .message-area');
407 var body = trim(message_area.val());
408 var xid = unescape(message_area.attr('data-to'));
410 // If the user didn't entered any message, stop
415 // We send the message through the XMPP network
416 var aMsg = new JSJaCMessage();
424 if(body.match(/^\/help\s*(.*)/)) {
426 var help_text = '<p class="help" xmlns="http://www.w3.org/1999/xhtml">';
427 help_text += '<b>' + _e("Available shortcuts:") + '</b>';
433 shortcuts.push(printf(_e("%s removes the chat logs"), '<em>/clear</em>'));
434 shortcuts.push(printf(_e("%s joins a groupchat"), '<em>/join jid</em>'));
435 shortcuts.push(printf(_e("%s closes the chat"), '<em>/part</em>'));
436 shortcuts.push(printf(_e("%s shows the user profile"), '<em>/whois jid</em>'));
438 // Groupchat shortcuts
439 if(type == 'groupchat') {
440 shortcuts.push(printf(_e("%s sends a message to the room"), '<em>/say message</em>'));
441 shortcuts.push(printf(_e("%s changes your nickname"), '<em>/nick nickname</em>'));
442 shortcuts.push(printf(_e("%s sends a message to someone in the room"), '<em>/msg nickname message</em>'));
443 shortcuts.push(printf(_e("%s changes the room topic"), '<em>/topic subject</em>'));
444 shortcuts.push(printf(_e("%s kicks an user of the room"), '<em>/kick nickname reason</em>'));
445 shortcuts.push(printf(_e("%s bans an user of the room"), '<em>/ban nickname reason</em>'));
446 shortcuts.push(printf(_e("%s invites someone to join the room"), '<em>/invite jid message</em>'));
449 // Generate the code from the array
450 shortcuts = shortcuts.sort();
453 help_text += shortcuts[s] + '<br />';
457 // Display the message
458 displayMessage(type, xid, hash, 'help', help_text, getCompleteTime(), getTimeStamp(), 'system-message', false);
461 chatStateSend('active', xid, hash);
465 else if(body.match(/^\/clear/)) {
466 cleanChat(hex_md5(xid));
469 chatStateSend('active', xid, hash);
473 else if(body.match(/^\/join (\S+)\s*(.*)/)) {
475 var room = generateXID(RegExp.$1, 'groupchat');
476 var pass = RegExp.$2;
478 checkChatCreate(room, 'groupchat');
481 chatStateSend('active', xid, hash);
485 else if(body.match(/^\/part\s*(.*)/) && (!isAnonymous() || (isAnonymous() && (xid != generateXID(ANONYMOUS_ROOM, 'groupchat')))))
486 quitThisChat(xid, hex_md5(xid), type);
489 else if(body.match(/^\/whois(( (\S+))|($))/)) {
490 var whois_xid = RegExp.$3;
493 if(type == 'groupchat') {
494 var nXID = getMUCUserXID(xid, whois_xid);
502 // Chat or private WHOIS
507 openUserInfos(whois_xid);
511 chatStateSend('active', xid, hash);
515 else if(type == 'chat') {
516 aMsg.setType('chat');
518 // Generates the correct message depending of the choosen style
520 var genMsg = generateMessage(aMsg, body, hash);
522 if(genMsg == 'XHTML')
526 var receipt_request = receiptRequest(hash);
529 aMsg.appendNode('request', {'xmlns': NS_URN_RECEIPTS});
532 aMsg.appendNode('active', {'xmlns': NS_CHATSTATES});
535 con.send(aMsg, handleErrorReply);
537 // Filter the xHTML message (for us!)
539 body = filterThisXHTML(aMsg.getNode());
541 // Finally we display the message we just sent
542 var my_xid = getXID();
544 displayMessage('chat', my_xid, hash, getBuddyName(my_xid).htmlEnc(), body, getCompleteTime(), getTimeStamp(), 'user-message', notXHTML, '', 'me', id);
548 checkReceived(hash, id);
551 // Groupchat message type
552 else if(type == 'groupchat') {
554 if(body.match(/^\/say (.+)/)) {
555 body = body.replace(/^\/say (.+)/, '$1');
557 aMsg.setType('groupchat');
558 generateMessage(aMsg, body, hash);
560 con.send(aMsg, handleErrorReply);
564 else if(body.match(/^\/nick (.+)/)) {
565 var nick = body.replace(/^\/nick (.+)/, '$1');
567 // Does not exist yet?
568 if(!getMUCUserXID(xid, nick)) {
569 // Send a new presence
570 sendPresence(xid + '/' + nick, '', getUserShow(), getUserStatus(), '', false, false, handleErrorReply);
572 // Change the stored nickname
573 $('#' + hex_md5(xid)).attr('data-nick', escape(nick));
576 chatStateSend('active', xid, hash);
581 else if(body.match(/^\/msg (\S+)\s+(.+)/)) {
582 var nick = RegExp.$1;
583 var body = RegExp.$2;
584 var nXID = getMUCUserXID(xid, nick);
586 // We check if the user exists
590 // If the private message is not empty
592 aMsg.setType('chat');
594 generateMessage(aMsg, body, hash);
596 con.send(aMsg, handleErrorReply);
601 else if(body.match(/^\/topic (.+)/)) {
602 var topic = body.replace(/^\/topic (.+)/, '$1');
604 aMsg.setType('groupchat');
605 aMsg.setSubject(topic);
607 con.send(aMsg, handleMessageError);
610 chatStateSend('active', xid, hash);
614 else if(body.match(/^\/ban (\S+)\s*(.*)/)) {
615 var nick = RegExp.$1;
616 var reason = RegExp.$2;
617 var nXID = getMUCUserRealXID(xid, nick);
619 // We check if the user exists
624 // We generate the ban IQ
625 var iq = new JSJaCIQ();
629 var iqQuery = iq.setQuery(NS_MUC_ADMIN);
630 var item = iqQuery.appendChild(iq.buildNode('item', {'affiliation': 'outcast', 'jid': nXID, 'xmlns': NS_MUC_ADMIN}));
633 item.appendChild(iq.buildNode('reason', {'xmlns': NS_MUC_ADMIN}, reason));
635 con.send(iq, handleErrorReply);
639 chatStateSend('active', xid, hash);
643 else if(body.match(/^\/kick (\S+)\s*(.*)/)) {
644 var nick = RegExp.$1;
645 var reason = RegExp.$2;
646 var nXID = getMUCUserXID(xid, nick);
648 // We check if the user exists
653 // We generate the kick IQ
654 var iq = new JSJaCIQ();
658 var iqQuery = iq.setQuery(NS_MUC_ADMIN);
659 var item = iqQuery.appendChild(iq.buildNode('item', {'nick': nick, 'role': 'none', 'xmlns': NS_MUC_ADMIN}));
662 item.appendChild(iq.buildNode('reason', {'xmlns': NS_MUC_ADMIN}, reason));
664 con.send(iq, handleErrorReply);
668 chatStateSend('active', xid, hash);
672 else if(body.match(/^\/invite (\S+)\s*(.*)/)) {
673 var i_xid = RegExp.$1;
674 var reason = RegExp.$2;
676 var x = aMsg.appendNode('x', {'xmlns': NS_MUC_USER});
677 var aNode = x.appendChild(aMsg.buildNode('invite', {'to': i_xid, 'xmlns': NS_MUC_USER}));
680 aNode.appendChild(aMsg.buildNode('reason', {'xmlns': NS_MUC_USER}, reason));
682 con.send(aMsg, handleErrorReply);
685 chatStateSend('active', xid, hash);
688 // No shortcut, this is a message
690 aMsg.setType('groupchat');
693 aMsg.appendNode('active', {'xmlns': NS_CHATSTATES});
695 generateMessage(aMsg, body, hash);
697 con.send(aMsg, handleMessageError);
699 logThis('Message sent to: ' + xid + ' / ' + type, 3);
703 // We reset the message input
704 $('#' + hash + ' .message-area').val('');
712 // Generates the correct message area style
713 function generateStyle(hash) {
714 // Initialize the vars
715 var styles = '#' + hash + ' div.bubble-style';
716 var checkbox = styles + ' input[type=checkbox]';
717 var color = styles + ' a.color.selected';
720 // Loop the input values
721 $(checkbox).filter(':checked').each(function() {
722 // If there is a previous element
726 // Get the current style
727 switch($(this).attr('class')) {
729 style += 'font-weight: bold;';
733 style += 'font-style: italic;';
737 style += 'text-decoration: underline;';
742 // Get the color value
743 $(color).each(function() {
744 style += 'color: #' + $(this).attr('data-color');
750 // Generates the correct message code
751 function generateMessage(aMsg, body, hash) {
752 // Create the classical body
756 var style = $('#' + hash + ' .message-area').attr('style');
758 // A message style is choosen
760 // Explode the message body new lines (to create one <p /> element by line)
761 var new_lines = new Array(body);
764 new_lines = body.split('\n');
766 // Create the XML elements
767 var aHtml = aMsg.appendNode('html', {'xmlns': NS_XHTML_IM});
768 var aBody = aHtml.appendChild(aMsg.buildNode('body', {'xmlns': NS_XHTML}));
770 // Use the exploded body array to create one element per entry
771 for(i in new_lines) {
773 var cLine = new_lines[i];
775 // Blank line, we put a <br />
776 if(cLine.match(/(^)(\s+)($)/) || !cLine)
777 aBody.appendChild(aMsg.buildNode('br', {'xmlns': NS_XHTML}));
779 // Line with content, we put a <p />
781 // HTML encode the line
782 cLine = cLine.htmlEnc();
785 cLine = applyLinks(cLine, 'xhtml-im', style);
787 // Append the filtered line
788 $(aBody).append($('<p style="' + style + '">' + cLine + '</p>'));
798 // Displays a given message in a chat tab
799 function displayMessage(type, xid, hash, name, body, time, stamp, message_type, is_xhtml, nick_quote, mode, id) {
800 // Generate some stuffs
801 var has_avatar = false;
807 if(message_type != 'system-message') {
809 xid_hash = hex_md5(xid);
813 var cont_scroll = document.getElementById('chat-content-' + hash);
814 var can_scroll = false;
816 if(!cont_scroll.scrollTop || ((cont_scroll.clientHeight + cont_scroll.scrollTop) == cont_scroll.scrollHeight))
823 data_id = ' data-id="' + id + '"';
825 // Filter the message
826 var filteredMessage = filterThisMessage(body, name, is_xhtml);
828 // Display the received message in the room
829 var messageCode = '<div class="one-line ' + message_type + nick_quote + '"' + data_id + '>';
831 // Name color attribute
832 if(type == 'groupchat')
833 attribute = ' style="color: ' + generateColor(name) + ';" class="name';
835 attribute = ' class="name';
838 attribute += ' ' + mode;
841 // Close the class attribute
842 if(message_type == 'system-message')
843 attribute += ' hidden"';
847 // Filter the previous displayed message
848 var last = $('#' + hash + ' .one-group:last');
849 var last_name = last.find('b.name').attr('data-xid');
850 var last_type = last.attr('data-type');
851 var last_stamp = parseInt(last.attr('data-stamp'));
854 // We can group it with another previous message
855 if((last_name == xid) && (message_type == last_type) && ((stamp - last_stamp) <= 1800))
858 // Is it a /me command?
859 if(body.match(/(^|>)(\/me )([^<]+)/))
860 filteredMessage = '<i>' + filteredMessage + '</i>';
862 messageCode += filteredMessage + '</div>';
865 var group_path = ' .one-group:last';
868 // Generate message headers
869 var message_head = '';
871 // Any avatar to add?
873 message_head += '<div class="avatar-container"><img class="avatar" src="' + './img/others/default-avatar.png' + '" alt="" /></div>';
875 // Add the date & the name
876 message_head += '<span class="date">' + time + '</span><b data-xid="' + encodeQuotes(xid) + '" ' + attribute + '>' + name + '</b>';
878 // Generate message code
880 messageCode = '<div class="one-group ' + xid_hash + '" data-type="' + message_type + '" data-stamp="' + stamp + '">' + message_head + messageCode + '</div>';
884 if(hash == 'archives')
885 $('#archives .logs' + group_path).append(messageCode);
889 // Write the code in the DOM
890 $('#' + hash + ' .content' + group_path).append(messageCode);
892 // Must get the avatar?
893 if(has_avatar && xid)
894 getAvatar(xid, 'cache', 'true', 'forget');
897 // Scroll to this message