]> git.mxchange.org Git - friendica-addons.git/blob - jappixmini/jappix/js/presence.js
Merge pull request #520 from rabuzarus/20180206_-_membersince_frio_support
[friendica-addons.git] / jappixmini / jappix / js / presence.js
1 /*
2
3 Jappix - An open social platform
4 These are the presence JS scripts for Jappix
5
6 -------------------------------------------------
7
8 License: AGPL
9 Author: Vanaryon
10 Last revision: 23/09/11
11
12 */
13
14 // Sends the user first presence
15 var FIRST_PRESENCE_SENT = false;
16
17 function firstPresence(checksum) {
18         logThis('First presence sent.', 3);
19         
20         // Jappix is now ready: change the title
21         pageTitle('talk');
22         
23         // Anonymous check
24         var is_anonymous = isAnonymous();
25         
26         // Update our marker
27         FIRST_PRESENCE_SENT = true;
28         
29         // Try to use the last status message
30         var status = getDB('options', 'presence-status');
31         
32         if(!status)
33                 status = '';
34         
35         // We tell the world that we are online
36         if(!is_anonymous)
37                 sendPresence('', '', '', status, checksum);
38         
39         // Any status to apply?
40         if(status)
41                 $('#presence-status').val(status);
42         
43         // Enable the presence picker
44         $('#presence-status').removeAttr('disabled');
45         $('#my-infos .f-presence a.picker').removeClass('disabled');
46         
47         // We set the last activity stamp
48         PRESENCE_LAST_ACTIVITY = getTimeStamp();
49         
50         // We store our presence
51         setDB('presence-show', 1, 'available');
52         
53         // Not anonymous
54         if(!is_anonymous) {
55                 // We get the stored bookmarks (because of the photo hash and some other stuffs, we must get it later)
56                 getStorage(NS_BOOKMARKS);
57                 
58                 // We open a new chat if a XMPP link was submitted
59                 if((parent.location.hash != '#OK') && LINK_VARS['x']) {
60                         // A link is submitted in the URL
61                         xmppLink(LINK_VARS['x']);
62                         
63                         // Set a OK status
64                         parent.location.hash = 'OK';
65                 }
66         }
67 }
68
69 // Handles incoming presence packets
70 function handlePresence(presence) {
71         // We define everything needed here
72         var from = fullXID(getStanzaFrom(presence));
73         var hash = hex_md5(from);
74         var node = presence.getNode();
75         var xid = bareXID(from);
76         var xidHash = hex_md5(xid);
77         
78         // We get the type content
79         var type = presence.getType();
80         if(!type)
81                 type = '';
82         
83         // We get the priority content
84         var priority = presence.getPriority() + '';
85         if(!priority || (type == 'error'))
86                 priority = '0';
87         
88         // We get the show content
89         var show = presence.getShow();
90         if(!show || (type == 'error'))
91                 show = '';
92         
93         // We get the status content
94         var status = presence.getStatus();
95         if(!status || (type == 'error'))
96                 status = '';
97         
98         // We get the photo content
99         var photo = $(node).find('x[xmlns=' + NS_VCARD_P + ']:first photo');
100         var checksum = photo.text();
101         var hasPhoto = photo.size();
102         
103         if(hasPhoto && (type != 'error'))
104                 hasPhoto = 'true';
105         else
106                 hasPhoto = 'false';
107         
108         // We get the CAPS content
109         var caps = $(node).find('c[xmlns=' + NS_CAPS + ']:first').attr('ver');
110         if(!caps || (type == 'error'))
111                 caps = '';
112         
113         // This presence comes from another resource of my account with a difference avatar checksum
114         if((xid == getXID()) && (hasPhoto == 'true') && (checksum != getDB('checksum', 1)))
115                 getAvatar(getXID(), 'force', 'true', 'forget');
116         
117         // This presence comes from a groupchat
118         if(isPrivate(xid)) {
119                 var x_muc = $(node).find('x[xmlns=' + NS_MUC_USER + ']:first');
120                 var item = x_muc.find('item');
121                 var affiliation = item.attr('affiliation');
122                 var role = item.attr('role');
123                 var reason = item.find('reason').text();
124                 var iXID = item.attr('jid');
125                 var iNick = item.attr('nick');
126                 var nick = thisResource(from);
127                 var messageTime = getCompleteTime();
128                 var notInitial = true;
129                 
130                 // Read the status code
131                 var status_code = new Array();
132                 
133                 x_muc.find('status').each(function() {
134                         status_code.push(parseInt($(this).attr('code')));
135                 });
136                 
137                 // If this is an initial presence (when user join the room)
138                 if(exists('#' + xidHash + '[data-initial=true]'))
139                         notInitial = false;
140                 
141                 // If one user is quitting
142                 if(type && (type == 'unavailable')) {
143                         displayMucPresence(from, xidHash, hash, type, show, status, affiliation, role, reason, status_code, iXID, iNick, messageTime, nick, notInitial);
144                         
145                         removeDB('presence', from);
146                 }
147                 
148                 // If one user is joining
149                 else {
150                         // Fixes M-Link first presence bug (missing ID!)
151                         if((nick == getMUCNick(xidHash)) && (presence.getID() == null) && !exists('#page-engine #' + xidHash + ' .list .' + hash)) {
152                                 handleMUC(presence);
153                                 
154                                 logThis('Passed M-Link MUC first presence handling.', 2);
155                         }
156                         
157                         else {
158                                 displayMucPresence(from, xidHash, hash, type, show, status, affiliation, role, reason, status_code, iXID, iNick, messageTime, nick, notInitial);
159                                 
160                                 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>';
161                                 
162                                 setDB('presence', from, xml);
163                         }
164                 }
165                 
166                 // Manage the presence
167                 presenceFunnel(from, hash);
168         }
169         
170         // This presence comes from an user or a gateway
171         else {
172                 // Subscribed & unsubscribed stanzas
173                 if((type == 'subscribed') || (type == 'unsubscribed'))
174                         return;
175                 
176                 // Subscribe stanza
177                 else if(type == 'subscribe') {
178                         // This is a buddy we can safely authorize, because we added him to our roster
179                         if(exists('#buddy-list .buddy[data-xid=' + escape(xid) + ']'))
180                                 acceptSubscribe(xid);
181                         
182                         // We do not know this entity, we'd be better ask the user
183                         else {
184                                 // Get the nickname
185                                 var nickname = $(node).find('nick[xmlns=' + NS_NICK + ']:first').text();
186                                 
187                                 // New notification
188                                 newNotification('subscribe', xid, [xid, nickname], status);
189                         }
190                 }
191                 
192                 // Unsubscribe stanza
193                 else if(type == 'unsubscribe')
194                         sendRoster(xid, 'remove');
195                 
196                 // Other stanzas
197                 else {
198                         // Unavailable/error presence
199                         if(type == 'unavailable')
200                                 removeDB('presence', from);
201                         
202                         // Other presence (available, subscribe...)
203                         else {
204                                 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>';
205                                 
206                                 setDB('presence', from, xml);
207                         }
208                         
209                         // We manage the presence
210                         presenceFunnel(xid, xidHash);
211                         
212                         // We display the presence in the current chat
213                         if(exists('#' + xidHash)) {
214                                 var dStatus = filterStatus(xid, status, false);
215                                 
216                                 if(dStatus)
217                                         dStatus = ' (' + dStatus + ')';
218                                 
219                                 // Generate the presence-in-chat code
220                                 var dName = getBuddyName(from).htmlEnc();
221                                 var dBody = dName + ' (' + from + ') ' + _e("is now") + ' ' + humanShow(show, type) + dStatus;
222                                 
223                                 // Check whether it has been previously displayed
224                                 var can_display = true;
225                                 
226                                 if($('#' + xidHash + ' .one-line.system-message:last').html() == dBody)
227                                         can_display = false;
228                                 
229                                 if(can_display)
230                                         displayMessage('chat', xid, xidHash, dName, dBody, getCompleteTime(), getTimeStamp(), 'system-message', false);
231                         }
232                 }
233         }
234         
235         // For logger
236         if(!show) {
237                 if(!type)
238                         show = 'available';
239                 else
240                         show = 'unavailable';
241         }
242         
243         logThis('Presence received: ' + show + ', from ' + from);
244 }
245
246 // Displays a MUC presence
247 function displayMucPresence(from, roomHash, hash, type, show, status, affiliation, role, reason, status_code, iXID, iNick, messageTime, nick, initial) {
248         // Generate the values
249         var thisUser = '#page-engine #' + roomHash + ' .list .' + hash;
250         var thisPrivate = $('#' + hash + ' .message-area');
251         var nick_html = nick.htmlEnc();
252         var real_xid = '';
253         var write = nick_html + ' ';
254         var notify = false;
255         
256         // Reset data?
257         if(!role)
258                 role = 'participant';
259         if(!affiliation)
260                 affiliation = 'none';
261         
262         // Must update the role?
263         if(exists(thisUser) && (($(thisUser).attr('data-role') != role) || ($(thisUser).attr('data-affiliation') != affiliation)))
264                 $(thisUser).remove();
265         
266         // Any XID submitted?
267         if(iXID) {
268                 real_xid = ' data-realxid="' + iXID + '"';
269                 iXID = bareXID(iXID);
270                 write += ' (<a onclick="return checkChatCreate(\'' + encodeOnclick(iXID) + '\', \'chat\');" href="xmpp:' + encodeOnclick(iXID) + '">' + iXID + '</a>) ';
271         }
272         
273         // User does not exists yet
274         if(!exists(thisUser) && (!type || (type == 'available'))) {
275                 var myself = '';
276                 
277                 // Is it me?
278                 if(nick == getMUCNick(roomHash)) {
279                         // Enable the room
280                         $('#' + roomHash + ' .message-area').removeAttr('disabled');
281                         
282                         // Marker
283                         myself = ' myself';
284                 }
285                 
286                 // Set the user in the MUC list
287                 $('#' + roomHash + ' .list .' + role + ' .title').after(
288                         '<div class="user ' + hash + myself + '" data-xid="' + encodeQuotes(from) + '" data-nick="' + escape(nick) + '"' + real_xid + ' data-role="' + encodeQuotes(role) + '" data-affiliation="' + encodeQuotes(affiliation) + '">' + 
289                                 '<div class="name talk-images available">' + nick_html + '</div>' + 
290                                 
291                                 '<div class="avatar-container">' + 
292                                         '<img class="avatar" src="' + './img/others/default-avatar.png' + '" alt="" />' + 
293                                 '</div>' + 
294                         '</div>'
295                 );
296                 
297                 // Click event
298                 if(nick != getMUCNick(roomHash))
299                         $(thisUser).live('click', function() {
300                                 checkChatCreate(from, 'private');
301                         });
302                 
303                 // We tell the user that someone entered the room
304                 if(!initial) {
305                         notify = true;
306                         write += _e("joined the chat room");
307                         
308                         // Any status?
309                         if(status)
310                                 write += ' (' + filterThisMessage(status, nick_html, true) + ')';
311                         else
312                                 write += ' (' + _e("no status") + ')';
313                 }
314                 
315                 // Enable the private chat input
316                 thisPrivate.removeAttr('disabled');
317         }
318         
319         else if((type == 'unavailable') || (type == 'error')) {
320                 // Is it me?
321                 if(nick == getMUCNick(roomHash)) {
322                         $(thisUser).remove();
323                         
324                         // Disable the groupchat input
325                         $('#' + roomHash + ' .message-area').attr('disabled', true);
326                         
327                         // Remove all the groupchat users
328                         $('#' + roomHash + ' .list .user').remove();
329                 }
330                 
331                 // Someone has been kicked or banned?
332                 if(existArrayValue(status_code, 301) || existArrayValue(status_code, 307)) {
333                         $(thisUser).remove();
334                         notify = true;
335                         
336                         // Kicked?
337                         if(existArrayValue(status_code, 307))
338                                 write += _e("has been kicked");
339                         
340                         // Banned?
341                         if(existArrayValue(status_code, 301))
342                                 write += _e("has been banned");
343                         
344                         // Any reason?
345                         if(reason)
346                                 write += ' (' + filterThisMessage(reason, nick_html, true) + ')';
347                         else
348                                 write += ' (' + _e("no reason") + ')';
349                 }
350                 
351                 // Nickname change?
352                 else if(existArrayValue(status_code, 303) && iNick) {
353                         notify = true;
354                         write += printf(_e("changed his/her nickname to %s"), iNick.htmlEnc());
355                         
356                         // New values
357                         var new_xid = cutResource(from) + '/' + iNick;
358                         var new_hash = hex_md5(new_xid);
359                         var new_class = 'user ' + new_hash;
360                         
361                         if($(thisUser).hasClass('myself'))
362                                 new_class += ' myself';
363                         
364                         // Die the click event
365                         $(thisUser).die('click');
366                         
367                         // Change to the new nickname
368                         $(thisUser).attr('data-nick', iNick)
369                                    .attr('data-xid', new_xid)
370                                    .find('.name').text(iNick);
371                         
372                         // Change the user class
373                         $(thisUser).attr('class', new_class);
374                         
375                         // New click event
376                         $('#page-engine #' + roomHash + ' .list .' + new_hash).live('click', function() {
377                                 checkChatCreate(new_xid, 'private');
378                         });
379                 }
380                 
381                 // We tell the user that someone left the room
382                 else if(!initial) {
383                         $(thisUser).remove();
384                         notify = true;
385                         write += _e("left the chat room");
386                         
387                         // Any status?
388                         if(status)
389                                 write += ' (' + filterThisMessage(status, nick_html, true) + ')';
390                         else
391                                 write += ' (' + _e("no status") + ')';
392                 }
393                 
394                 // Disable the private chat input
395                 thisPrivate.attr('disabled', true);
396         }
397         
398         // Must notify something
399         if(notify)
400                 displayMessage('groupchat', from, roomHash, nick_html, write, messageTime, getTimeStamp(), 'system-message', false);
401         
402         // Set the good status show icon
403         switch(show) {
404                 case 'chat':
405                 case 'away':
406                 case 'xa':
407                 case 'dnd':
408                         break;
409                 
410                 default:
411                         show = 'available';
412                         break;
413         }
414         
415         $(thisUser + ' .name').attr('class', 'name talk-images ' + show);
416         
417         // Set the good status text
418         var uTitle = nick;
419         
420         // Any XID to add?
421         if(iXID)
422                 uTitle += ' (' + iXID + ')';
423         
424         // Any status to add?
425         if(status)
426                 uTitle += ' - ' + status;
427         
428         $(thisUser).attr('title', uTitle);
429         
430         // Show or hide the role category, depending of its content
431         $('#' + roomHash + ' .list .role').each(function() {
432                 if($(this).find('.user').size())
433                         $(this).show();
434                 else
435                         $(this).hide();
436         });
437 }
438
439 // Filters a given status
440 function filterStatus(xid, status, cut) {
441         var dStatus = '';
442         
443         if(!status)
444                 status = '';
445         
446         else {
447                 if(cut)
448                         dStatus = truncate(status, 50);
449                 else
450                         dStatus = status;
451                 
452                 dStatus = filterThisMessage(dStatus, getBuddyName(xid).htmlEnc(), true);
453         }
454         
455         return dStatus;
456 }
457
458 // Displays a user's presence
459 function displayPresence(value, type, show, status, hash, xid, avatar, checksum, caps) {
460         // Display the presence in the roster
461         var path = '#buddy-list .' + hash;
462         var buddy = $('#buddy-list .content .' + hash);
463         var dStatus = filterStatus(xid, status, false);
464         var tStatus = encodeQuotes(status);
465         var biStatus;
466         
467         // The buddy presence behind his name
468         $(path + ' .name .buddy-presence').replaceWith('<p class="buddy-presence talk-images ' + type + '">' + value + '</p>');
469         
470         // The buddy presence in the buddy infos
471         if(dStatus)
472                 biStatus = dStatus;
473         else
474                 biStatus = value;
475         
476         $(path + ' .bi-status').replaceWith('<p class="bi-status talk-images ' + type + '" title="' + tStatus + '">' + biStatus + '</p>');
477         
478         // When the buddy disconnect himself, we hide him
479         if((type == 'unavailable') || (type == 'error')) {
480                 // Set a special class to the buddy
481                 buddy.addClass('hidden-buddy');
482                 
483                 // No filtering is launched?
484                 if(!SEARCH_FILTERED)
485                         buddy.hide();
486                 
487                 // All the buddies are shown?
488                 if(BLIST_ALL)
489                         buddy.show();
490                 
491                 // Chat stuffs
492                 if(exists('#' + hash)) {
493                         // Remove the chatstate stuffs
494                         resetChatState(hash);
495                         $('#' + hash + ' .chatstate').remove();
496                         $('#' + hash + ' .message-area').removeAttr('data-chatstates');
497                         
498                         // Get the buddy avatar (only if a chat is opened)
499                         getAvatar(xid, 'cache', 'true', 'forget');
500                 }
501         }
502         
503         // If the buddy is online
504         else {
505                 // When the buddy is online, we show it
506                 buddy.removeClass('hidden-buddy');
507                 
508                 // No filtering is launched?
509                 if(!SEARCH_FILTERED)
510                         buddy.show();
511                 
512                 // Get the online buddy avatar if not a gateway
513                 getAvatar(xid, 'cache', avatar, checksum);
514         }
515         
516         // Display the presence in the chat
517         if(exists('#' + hash)) {
518                 // We generate a well formed status message
519                 if(dStatus) {
520                         // No need to write the same status two times
521                         if(dStatus == value)
522                                 dStatus = '';
523                         else
524                                 dStatus = ' (' + dStatus + ')';
525                 }
526                 
527                 // We show the presence value
528                 $('#' + hash + ' .bc-infos').replaceWith('<p class="bc-infos" title="' + tStatus + '"><span class="' + type + ' show talk-images">' + value + '</span>' + dStatus + '</p>');
529                 
530                 // Process the new status position
531                 adaptChatPresence(hash);
532                 
533                 // Get the disco#infos for this user
534                 var highest = getHighestResource(xid);
535                 
536                 if(highest)
537                         getDiscoInfos(highest, caps);
538                 else
539                         displayDiscoInfos(xid, '');
540         }
541         
542         // Display the presence in the switcher
543         if(exists('#page-switch .' + hash))
544                 $('#page-switch .' + hash + ' .icon').removeClass('available unavailable error away busy').addClass(type);
545         
546         // Update roster groups
547         if(!SEARCH_FILTERED)
548                 updateGroups();
549         else
550                 funnelFilterBuddySearch();
551 }
552
553 // Process the chat presence position
554 function adaptChatPresence(hash) {
555         // Get values
556         var pep_numb = $('#' + hash + ' .bc-pep').find('a').size();
557         
558         // Process the right position
559         var presence_right = 12;
560         
561         if(pep_numb)
562                 presence_right = (pep_numb * 20) + 18;
563         
564         // Apply the right position
565         $('#' + hash + ' p.bc-infos').css('right', presence_right);
566 }
567
568 // Convert the presence "show" element into a human-readable output
569 function humanShow(show, type) {
570         if(type == 'unavailable')
571                 show = _e("Unavailable");
572         
573         else if(type == 'error')
574                 show = _e("Error");
575         
576         else {
577                 switch(show) {
578                         case 'chat':
579                                 show = _e("Talkative");
580                                 break;
581                         
582                         case 'away':
583                                 show = _e("Away");
584                                 break;
585                         
586                         case 'xa':
587                                 show = _e("Not available");
588                                 break;
589                         
590                         case 'dnd':
591                                 show = _e("Busy");
592                                 break;
593                         
594                         default:
595                                 show = _e("Available");
596                                 break;
597                 }
598         }
599         
600         return show;
601 }
602
603 // Makes the presence data go in the right way
604 function presenceIA(type, show, status, hash, xid, avatar, checksum, caps) {
605         // Is there a status defined?
606         if(!status)
607                 status = humanShow(show, type);
608         
609         // Then we can handle the events
610         if(type == 'error')
611                 displayPresence(_e("Error"), 'error', show, status, hash, xid, avatar, checksum, caps);
612         
613         else if(type == 'unavailable')
614                 displayPresence(_e("Unavailable"), 'unavailable', show, status, hash, xid, avatar, checksum, caps);
615         
616         else {
617                 switch(show) {
618                         case 'chat':
619                                 displayPresence(_e("Talkative"), 'available', show, status, hash, xid, avatar, checksum, caps);
620                                 break;
621                         
622                         case 'away':
623                                 displayPresence(_e("Away"), 'away', show, status, hash, xid, avatar, checksum, caps);
624                                 break;
625                         
626                         case 'xa':
627                                 displayPresence(_e("Not available"), 'busy', show, status, hash, xid, avatar, checksum, caps);
628                                 break;
629                         
630                         case 'dnd':
631                                 displayPresence(_e("Busy"), 'busy', show, status, hash, xid, avatar, checksum, caps);
632                                 break;
633                         
634                         default:
635                                 displayPresence(_e("Available"), 'available', show, status, hash, xid, avatar, checksum, caps);
636                                 break;
637                 }
638         }
639 }
640
641 // Gets the highest resource priority for an user
642 function highestPriority(xid) {
643         var maximum = null;
644         var selector, priority, type, highest;
645         
646         // This is a groupchat presence
647         if(xid.indexOf('/') != -1)
648                 highest = XMLFromString(getDB('presence', xid));
649         
650         // This is a "normal" presence: get the highest priority resource
651         else {
652                 for(var i = 0; i < sessionStorage.length; i++) {
653                         // Get the pointer values
654                         var current = sessionStorage.key(i);
655                         
656                         // If the pointer is on a stored presence
657                         if(explodeThis('_', current, 0) == 'presence') {
658                                 // Get the current XID
659                                 var now = bareXID(explodeThis('_', current, 1));
660                                 
661                                 // If the current XID equals the asked XID
662                                 if(now == xid) {
663                                         var xml = XMLFromString(sessionStorage.getItem(current));
664                                         var priority = parseInt($(xml).find('priority').text());
665                                         
666                                         // Higher priority
667                                         if((priority >= maximum) || (maximum == null)) {
668                                                 maximum = priority;
669                                                 highest = xml;
670                                         }
671                                 }
672                         }
673                 }
674         }
675         
676         // The user might be offline if no highest
677         if(!highest)
678                 highest = XMLFromString('<presence><type>unavailable</type></presence>');
679         
680         return highest;
681 }
682
683 // Gets the resource from a XID which has the highest priority
684 function getHighestResource(xid) {
685         var xml = $(highestPriority(xid));
686         var highest = xml.find('presence').attr('from');
687         var type = xml.find('type').text();
688         
689         // If the use is online, we can return its highest resource
690         if(!type || (type == 'available') || (type == 'null'))
691                 return highest;
692         else
693                 return false;
694 }
695
696 // Makes something easy to process for the presence IA
697 function presenceFunnel(xid, hash) {
698         // Get the highest priority presence value
699         var xml = $(highestPriority(xid));
700         var type = xml.find('type').text();
701         var show = xml.find('show').text();
702         var status = xml.find('status').text();
703         var avatar = xml.find('avatar').text();
704         var checksum = xml.find('checksum').text();
705         var caps = xml.find('caps').text();
706         
707         // Display the presence with that stored value
708         if(!type && !show)
709                 presenceIA('', 'available', status, hash, xid, avatar, checksum, caps);
710         else
711                 presenceIA(type, show, status, hash, xid, avatar, checksum, caps);
712 }
713
714 // Sends a defined presence packet
715 function sendPresence(to, type, show, status, checksum, limit_history, password, handle) {
716         // Get some stuffs
717         var priority = getDB('priority', 1);
718         
719         if(!priority)
720                 priority = '1';
721         if(!checksum)
722                 checksum = getDB('checksum', 1);
723         if(show == 'available')
724                 show = '';
725         if(type == 'available')
726                 type = '';
727         
728         // New presence
729         var presence = new JSJaCPresence();
730         
731         // Avoid "null" or "none" if nothing stored
732         if(!checksum || (checksum == 'none'))
733                 checksum = '';
734         
735         // Presence headers
736         if(to)
737                 presence.setTo(to);
738         if(type)
739                 presence.setType(type);
740         if(show)
741                 presence.setShow(show);
742         if(status)
743                 presence.setStatus(status);
744         
745         presence.setPriority(priority);
746         
747         // CAPS (entity capabilities)
748         presence.appendNode('c', {'xmlns': NS_CAPS, 'hash': 'sha-1', 'node': 'https://www.jappix.com/', 'ver': myCaps()});
749         
750         // Nickname
751         var nickname = getName();
752         
753         if(nickname)
754                 presence.appendNode('nick', {'xmlns': NS_NICK}, nickname);
755         
756         // vcard-temp:x:update node
757         var x = presence.appendNode('x', {'xmlns': NS_VCARD_P});
758         x.appendChild(presence.buildNode('photo', {'xmlns': NS_VCARD_P}, checksum));
759         
760         // MUC X data
761         if(limit_history || password) {
762                 var xMUC = presence.appendNode('x', {'xmlns': NS_MUC});
763                 
764                 // Max messages age (for MUC)
765                 if(limit_history)
766                         xMUC.appendChild(presence.buildNode('history', {'maxstanzas': 20, 'seconds': 86400, 'xmlns': NS_MUC}));
767                 
768                 // Room password
769                 if(password)
770                         xMUC.appendChild(presence.buildNode('password', {'xmlns': NS_MUC}, password));
771         }
772         
773         // If away, send a last activity time
774         if((show == 'away') || (show == 'xa')) {
775                 /* REF: http://xmpp.org/extensions/xep-0256.html */
776                 
777                 presence.appendNode(presence.buildNode('query', {
778                         'xmlns': NS_LAST,
779                         'seconds': getPresenceLast()
780                 }));
781         }
782         
783         // Else, set a new last activity stamp
784         else
785                 PRESENCE_LAST_ACTIVITY = getTimeStamp();
786         
787         // Send the presence packet
788         if(handle)
789                 con.send(presence, handle);
790         else
791                 con.send(presence);
792         
793         if(!type)
794                 type = 'available';
795         
796         logThis('Presence sent: ' + type, 3);
797 }
798
799 // Performs all the actions to get the presence data
800 function presenceSend(checksum, autoidle) {
801         // We get the values of the inputs
802         var show = getUserShow();
803         var status = getUserStatus();
804         
805         // Send the presence
806         if(!isAnonymous())
807                 sendPresence('', '', show, status, checksum);
808         
809         // We set the good icon
810         presenceIcon(show);
811         
812         // We store our presence
813         if(!autoidle)
814                 setDB('presence-show', 1, show);
815         
816         // We send the presence to our active MUC
817         $('.page-engine-chan[data-type=groupchat]').each(function() {
818                 var tmp_nick = $(this).attr('data-nick');
819                 
820                 if(!tmp_nick)
821                         return;
822                 
823                 var room = unescape($(this).attr('data-xid'));
824                 var nick = unescape(tmp_nick);
825                 
826                 // Must re-initialize?
827                 if(RESUME)
828                         getMUC(room, nick);
829                 
830                 // Not disabled?
831                 else if(!$(this).find('.message-area').attr('disabled'))
832                         sendPresence(room + '/' + nick, '', show, status, '', true);
833         });
834 }
835
836 // Changes the presence icon
837 function presenceIcon(value) {
838         $('#my-infos .f-presence a.picker').attr('data-value', value);
839 }
840
841 // Sends a subscribe stanza
842 function sendSubscribe(to, type) {
843         var status = '';
844         
845         // Subscribe request?
846         if(type == 'subscribe')
847                 status = printf(_e("Hi, I am %s, I would like to add you as my friend."), getName());
848         
849         sendPresence(to, type, '', status);
850 }
851
852 // Accepts the subscription from another entity
853 function acceptSubscribe(xid, name) {
854         // We update our chat
855         $('#' + hex_md5(xid) + ' .tools-add').hide();
856         
857         // We send a subsribed presence (to confirm)
858         sendSubscribe(xid, 'subscribed');
859         
860         // We send a subscription request (subscribe both sides)
861         sendSubscribe(xid, 'subscribe');
862         
863         // Specify the buddy name (if any)
864         if(name)
865                 sendRoster(xid, '', name)
866 }
867
868 // Sends automatic away presence
869 var AUTO_IDLE = false;
870
871 function autoIdle() {
872         // Not connected?
873         if(!isConnected())
874                 return;
875         
876         // Stop if an xa presence was set manually
877         var last_presence = getUserShow();
878         
879         if(!AUTO_IDLE && ((last_presence == 'away') || (last_presence == 'xa')))
880                 return;
881         
882         var idle_presence;
883         var activity_limit;
884         
885         // Can we extend to auto extended away mode (20 minutes)?
886         if(AUTO_IDLE && (last_presence == 'away')) {
887                 idle_presence = 'xa';
888                 activity_limit = 1200;
889         }
890         
891         // We must set the user to auto-away (10 minutes)
892         else {
893                 idle_presence = 'away';
894                 activity_limit = 600;
895         }
896         
897         // The user is really inactive and has set another presence than extended away
898         if(((!AUTO_IDLE && (last_presence != 'away')) || (AUTO_IDLE && (last_presence == 'away'))) && (getLastActivity() >= activity_limit)) {
899                 // Then tell we use an auto presence
900                 AUTO_IDLE = true;
901                 
902                 // Get the old status message
903                 var status = getDB('options', 'presence-status');
904                 
905                 if(!status)
906                         status = '';
907                 
908                 // Change the presence input
909                 $('#my-infos .f-presence a.picker').attr('data-value', idle_presence);
910                 $('#presence-status').val(status);
911                 
912                 // Then send the xa presence
913                 presenceSend('', true);
914                 
915                 logThis('Auto-idle presence sent: ' + idle_presence, 3);
916         }
917 }
918
919 // Restores the old presence on a document bind
920 function eventIdle() {
921         // If we were idle, restore our old presence
922         if(AUTO_IDLE) {
923                 // Get the values
924                 var show = getDB('presence-show', 1);
925                 var status = getDB('options', 'presence-status');
926                 
927                 // Change the presence input
928                 $('#my-infos .f-presence a.picker').attr('data-value', show);
929                 $('#presence-status').val(status);
930                 $('#presence-status').placeholder();
931                 
932                 // Then restore the old presence
933                 presenceSend('', true);
934                 
935                 if(!show)
936                         show = 'available';
937                 
938                 logThis('Presence restored: ' + show, 3);
939         }
940         
941         // Apply some values
942         AUTO_IDLE = false;
943         LAST_ACTIVITY = getTimeStamp();
944 }
945
946 // Lives the auto idle functions
947 function liveIdle() {
948         // Apply the autoIdle function every minute
949         AUTO_IDLE = false;
950         $('#my-infos .f-presence').everyTime('30s', autoIdle);
951         
952         // On body bind (click & key event)
953         $('body').live('mousedown', eventIdle)
954                  .live('mousemove', eventIdle)
955                  .live('keydown', eventIdle);
956 }
957
958 // Kills the auto idle functions
959 function dieIdle() {
960         // Remove the event detector
961         $('body').die('mousedown', eventIdle)
962                  .die('mousemove', eventIdle)
963                  .die('keydown', eventIdle);
964 }
965
966 // Gets the user presence show
967 function getUserShow() {
968         return $('#my-infos .f-presence a.picker').attr('data-value');
969 }
970
971 // Gets the user presence status
972 function getUserStatus() {
973         return $('#presence-status').val();
974 }
975
976 // Addon launcher
977 function launchPresence() {
978         // Click event for user presence show
979         $('#my-infos .f-presence a.picker').click(function() {
980                 // Disabled?
981                 if($(this).hasClass('disabled'))
982                         return false;
983                 
984                 // Initialize some vars
985                 var path = '#my-infos .f-presence div.bubble';
986                 var show_id = ['xa', 'away', 'available'];
987                 var show_lang = [_e("Not available"), _e("Away"), _e("Available")];
988                 var show_val = getUserShow();
989                 
990                 // Yet displayed?
991                 var can_append = true;
992                 
993                 if(exists(path))
994                         can_append = false;
995                 
996                 // Add this bubble!
997                 showBubble(path);
998                 
999                 if(!can_append)
1000                         return false;
1001                 
1002                 // Generate the HTML code
1003                 var html = '<div class="bubble removable">';
1004                 
1005                 for(i in show_id) {
1006                         // Yet in use: no need to display it!
1007                         if(show_id[i] == show_val)
1008                                 continue;
1009                         
1010                         html += '<a href="#" class="talk-images" data-value="' + show_id[i] + '" title="' + show_lang[i] + '"></a>';
1011                 }
1012                 
1013                 html += '</div>';
1014                 
1015                 // Append the HTML code
1016                 $('#my-infos .f-presence').append(html);
1017                 
1018                 // Click event
1019                 $(path + ' a').click(function() {
1020                         // Update the presence show marker
1021                         $('#my-infos .f-presence a.picker').attr('data-value', $(this).attr('data-value'));
1022                         
1023                         // Close the bubble
1024                         closeBubbles();
1025                         
1026                         // Focus on the status input
1027                         $(document).oneTime(10, function() {
1028                                 $('#presence-status').focus();
1029                         });
1030                         
1031                         return false;
1032                 });
1033                 
1034                 return false;
1035         });
1036         
1037         // Submit events for user presence status
1038         $('#presence-status').placeholder()
1039         
1040         .keyup(function(e) {
1041                 if(e.keyCode == 13) {
1042                         $(this).blur();
1043                         
1044                         return false;
1045                 }
1046         })
1047         
1048         .blur(function() {
1049                 // Read the parameters
1050                 var show = getUserShow();
1051                 var status = getUserStatus();
1052                 
1053                 // Read the old parameters
1054                 var old_show = getDB('presence-show', 1);
1055                 var old_status = getDB('options', 'presence-status');
1056                 
1057                 // Must send the presence?
1058                 if((show != old_show) || (status != old_status)) {
1059                         // Update the local stored status
1060                         setDB('options', 'presence-status', status);
1061                         
1062                         // Update the server stored status
1063                         if(status != old_status)
1064                                 storeOptions();
1065                         
1066                         // Send the presence
1067                         presenceSend();
1068                 }
1069         })
1070         
1071         // Input focus handler
1072         .focus(function() {
1073                 closeBubbles();
1074         });
1075 }