]> git.mxchange.org Git - friendica-addons.git/blob - jappixmini/jappix/js/mini.js
0bfb80870c9b3dcfc6946d01e4ddf74bb5c55e76
[friendica-addons.git] / jappixmini / jappix / js / mini.js
1 /*
2
3 Jappix - An open social platform
4 These are the Jappix Mini JS scripts for Jappix
5
6 -------------------------------------------------
7
8 License: AGPL
9 Author: Vanaryon
10 Last revision: 04/08/11
11
12 */
13
14 // Jappix Mini vars
15 var MINI_DISCONNECT     = false;
16 var MINI_AUTOCONNECT    = false;
17 var MINI_SHOWPANE       = false;
18 var MINI_INITIALIZED    = false;
19 var MINI_ANONYMOUS      = false;
20 var MINI_ANIMATE        = false;
21 var MINI_NICKNAME       = null;
22 var MINI_TITLE          = null;
23 var MINI_DOMAIN         = null;
24 var MINI_USER           = null;
25 var MINI_PASSWORD       = null;
26 var MINI_RECONNECT      = 0;
27 var MINI_GROUPCHATS     = [];
28 var MINI_PASSWORDS      = [];
29 var MINI_RESOURCE       = JAPPIX_RESOURCE + ' Mini';
30
31 // Setups connection handlers
32 function setupConMini(con) {
33         con.registerHandler('message', handleMessageMini);
34         con.registerHandler('presence', handlePresenceMini);
35         con.registerHandler('iq', handleIQMini);
36         con.registerHandler('onerror', handleErrorMini);
37         con.registerHandler('onconnect', connectedMini);
38 }
39
40 // Connects the user with the given logins
41 function connectMini(domain, user, password) {
42         try {
43                 // We define the http binding parameters
44                 oArgs = new Object();
45                 
46                 if(HOST_BOSH_MINI)
47                         oArgs.httpbase = HOST_BOSH_MINI;
48                 else
49                         oArgs.httpbase = HOST_BOSH;
50                 
51                 // We create the new http-binding connection
52                 con = new JSJaCHttpBindingConnection(oArgs);
53                 
54                 // And we handle everything that happen
55                 setupConMini(con);
56                 
57                 // Generate a resource
58                 var random_resource = getDB('jappix-mini', 'resource');
59                 
60                 if(!random_resource)
61                         random_resource = MINI_RESOURCE + ' (' + (new Date()).getTime() + ')';
62                 
63                 // We retrieve what the user typed in the login inputs
64                 oArgs = new Object();
65                 oArgs.secure = true;
66                 oArgs.xmllang = XML_LANG;
67                 oArgs.resource = random_resource;
68                 oArgs.domain = domain;
69                 
70                 // Store the resource (for reconnection)
71                 setDB('jappix-mini', 'resource', random_resource);
72                 
73                 // Anonymous login?
74                 if(MINI_ANONYMOUS) {
75                         // Anonymous mode disabled?
76                         if(!allowedAnonymous()) {
77                                 logThis('Not allowed to use anonymous mode.', 2);
78                                 
79                                 // Notify this error
80                                 notifyErrorMini();
81                                 
82                                 return false;
83                         }
84                         
85                         // Bad domain?
86                         else if(lockHost() && (domain != HOST_ANONYMOUS)) {
87                                 logThis('Not allowed to connect to this anonymous domain: ' + domain, 2);
88                                 
89                                 // Notify this error
90                                 notifyErrorMini();
91                                 
92                                 return false;
93                         }
94                         
95                         oArgs.authtype = 'saslanon';
96                 }
97                 
98                 // Normal login
99                 else {
100                         // Bad domain?
101                         if(lockHost() && (domain != HOST_MAIN)) {
102                                 logThis('Not allowed to connect to this main domain: ' + domain, 2);
103                                 
104                                 // Notify this error
105                                 notifyErrorMini();
106                                 
107                                 return false;
108                         }
109                         
110                         // No nickname?
111                         if(!MINI_NICKNAME)
112                                 MINI_NICKNAME = user;
113                         
114                         oArgs.username = user;
115                         oArgs.pass = password;
116                 }
117                 
118                 // We connect !
119                 con.connect(oArgs);
120                 
121                 logThis('Jappix Mini is connecting...', 3);
122         }
123         
124         catch(e) {
125                 // Logs errors
126                 logThis('Error while logging in: ' + e, 1);
127                 
128                 // Reset Jappix Mini
129                 disconnectedMini();
130         }
131         
132         finally {
133                 return false;
134         }
135 }
136
137 // When the user is connected
138 function connectedMini() {
139         // Update the roster
140         jQuery('#jappix_mini a.jm_pane.jm_button span.jm_counter').text('0');
141         
142         // Do not get the roster if anonymous
143         if(MINI_ANONYMOUS)
144                 initializeMini();
145         else
146                 getRosterMini();
147         
148         // For logger
149         if(MINI_RECONNECT)
150                 logThis('Jappix Mini is now reconnected.', 3);
151         else
152                 logThis('Jappix Mini is now connected.', 3);
153         
154         // Reset reconnect var
155         MINI_RECONNECT = 0;
156 }
157
158 // When the user disconnects
159 function saveSessionMini() {
160         // Not connected?
161         if(!isConnected())
162                 return;
163         
164         // Save the actual Jappix Mini DOM
165         setDB('jappix-mini', 'dom', jQuery('#jappix_mini').html());
166         setDB('jappix-mini', 'nickname', MINI_NICKNAME);
167         
168         // Save the scrollbar position
169         var scroll_position = '';
170         var scroll_hash = jQuery('#jappix_mini div.jm_conversation:has(a.jm_pane.jm_clicked)').attr('data-hash');
171         
172         if(scroll_hash)
173                 scroll_position = document.getElementById('received-' + scroll_hash).scrollTop + '';
174         
175         setDB('jappix-mini', 'scroll', scroll_position);
176         
177         // Save the session stamp
178         setDB('jappix-mini', 'stamp', getTimeStamp());
179         
180         // Suspend connection
181         con.suspend(false);
182         
183         logThis('Jappix Mini session save tool launched.', 3);
184 }
185
186 // Disconnects the connected user
187 function disconnectMini() {
188         // No connection?
189         if(!isConnected())
190                 return false;
191         
192         // Change markers
193         MINI_DISCONNECT = true;
194         MINI_INITIALIZED = false;
195         
196         // Add disconnection handler
197         con.registerHandler('ondisconnect', disconnectedMini);
198         
199         // Disconnect the user
200         con.disconnect();
201         
202         logThis('Jappix Mini is disconnecting...', 3);
203         
204         return false;
205 }
206
207 // When the user is disconnected
208 function disconnectedMini() {
209         // Remove the stored items
210         removeDB('jappix-mini', 'dom');
211         removeDB('jappix-mini', 'nickname');
212         removeDB('jappix-mini', 'scroll');
213         removeDB('jappix-mini', 'stamp');
214         
215         // Connection error?
216         if(!MINI_DISCONNECT || MINI_INITIALIZED) {
217                 // Browser error?
218                 notifyErrorMini();
219                 
220                 // Reset reconnect timer
221                 jQuery('#jappix_mini').stopTime();
222                 
223                 // Try to reconnect after a while
224                 if(MINI_INITIALIZED && (MINI_RECONNECT < 5)) {
225                         // Reconnect interval
226                         var reconnect_interval = 10;
227                         
228                         if(MINI_RECONNECT)
229                                 reconnect_interval = (5 + (5 * MINI_RECONNECT)) * 1000;
230                         
231                         MINI_RECONNECT++;
232                         
233                         // Set timer
234                         jQuery('#jappix_mini').oneTime(reconnect_interval, function() {
235                                 launchMini(true, MINI_SHOWPANE, MINI_DOMAIN, MINI_USER, MINI_PASSWORD);
236                         });
237                 }
238         }
239         
240         // Normal disconnection?
241         else
242                 launchMini(false, MINI_SHOWPANE, MINI_DOMAIN, MINI_USER, MINI_PASSWORD);
243         
244         // Reset markers
245         MINI_DISCONNECT = false;
246         MINI_INITIALIZED = false;
247         
248         logThis('Jappix Mini is now disconnected.', 3);
249 }
250
251 // Handles the incoming messages
252 function handleMessageMini(msg) {
253         var type = msg.getType();
254         
255         // This is a message Jappix can handle
256         if((type == 'chat') || (type == 'normal') || (type == 'groupchat') || !type) {
257                 // Get the body
258                 var body = trim(msg.getBody());
259                 
260                 // Any subject?
261                 var subject = trim(msg.getSubject());
262                 
263                 if(subject)
264                         body = subject;
265                 
266                 if(body) {
267                         // Get the values
268                         var from = fullXID(getStanzaFrom(msg));
269                         var xid = bareXID(from);
270                         var use_xid = xid;
271                         var hash = hex_md5(xid);
272                         var nick = thisResource(from);
273                         
274                         // Read the delay
275                         var delay = readMessageDelay(msg.getNode());
276                         var d_stamp;
277                         
278                         // Manage this delay
279                         if(delay) {
280                                 time = relativeDate(delay);
281                                 d_stamp = Date.jab2date(delay);
282                         }
283                         
284                         else {
285                                 time = getCompleteTime();
286                                 d_stamp = new Date();
287                         }
288                         
289                         // Get the stamp
290                         var stamp = extractStamp(d_stamp);
291                         
292                         // Is this a groupchat private message?
293                         if(exists('#jappix_mini #chat-' + hash + '[data-type=groupchat]')) {
294                                 // Regenerate some stuffs
295                                 if((type == 'chat') || !type) {
296                                         xid = from;
297                                         hash = hex_md5(xid);
298                                 }
299                                 
300                                 // XID to use for a groupchat
301                                 else
302                                         use_xid = from;
303                         }
304                         
305                         // Message type
306                         var message_type = 'user-message';
307                         
308                         // Grouphat values
309                         if(type == 'groupchat') {
310                                 // Old message
311                                 if(msg.getChild('delay', NS_URN_DELAY) || msg.getChild('x', NS_DELAY))
312                                         message_type = 'old-message';
313                                 
314                                 // System message?
315                                 if(!nick || subject) {
316                                         nick = '';
317                                         message_type = 'system-message';
318                                 }
319                         }
320                         
321                         // Chat values
322                         else {
323                                 nick = jQuery('#jappix_mini a#friend-' + hash).text().revertHtmlEnc();
324                                 
325                                 // No nickname?
326                                 if(!nick)
327                                         nick = getXIDNick(xid);
328                         }
329                         
330                         // Define the target div
331                         var target = '#jappix_mini #chat-' + hash;
332                         
333                         // Create the chat if it does not exist
334                         if(!exists(target) && (type != 'groupchat'))
335                                 chatMini(type, xid, nick, hash);
336                         
337                         // Display the message
338                         displayMessageMini(type, body, use_xid, nick, hash, time, stamp, message_type);
339                         
340                         // Notify the user if not focused & the message is not a groupchat old one
341                         if((!jQuery(target + ' a.jm_chat-tab').hasClass('jm_clicked') || !isFocused()) && (message_type == 'user-message'))
342                                 notifyMessageMini(hash);
343                         
344                         logThis('Message received from: ' + from);
345                 }
346         }
347 }
348
349 // Handles the incoming IQs
350 function handleIQMini(iq) {
351         // Define some variables
352         var iqFrom = fullXID(getStanzaFrom(iq));
353         var iqID = iq.getID();
354         var iqQueryXMLNS = iq.getQueryXMLNS();
355         var iqType = iq.getType();
356         var iqNode = iq.getNode();
357         
358         // Build the response
359         var iqResponse = new JSJaCIQ();
360         
361         iqResponse.setID(iqID);
362         iqResponse.setTo(iqFrom);
363         iqResponse.setType('result');
364         
365         // Software version query
366         if((iqQueryXMLNS == NS_VERSION) && (iqType == 'get')) {
367                 /* REF: http://xmpp.org/extensions/xep-0092.html */
368                 
369                 var iqQuery = iqResponse.setQuery(NS_VERSION);
370                 
371                 iqQuery.appendChild(iq.buildNode('name', {'xmlns': NS_VERSION}, 'Jappix Mini'));
372                 iqQuery.appendChild(iq.buildNode('version', {'xmlns': NS_VERSION}, JAPPIX_VERSION));
373                 iqQuery.appendChild(iq.buildNode('os', {'xmlns': NS_VERSION}, BrowserDetect.OS));
374                 
375                 con.send(iqResponse);
376                 
377                 logThis('Received software version query: ' + iqFrom);
378         }
379         
380         // Roster push
381         else if((iqQueryXMLNS == NS_ROSTER) && (iqType == 'set')) {
382                 // Display the friend
383                 handleRosterMini(iq);
384                 
385                 con.send(iqResponse);
386                 
387                 logThis('Received a roster push.');
388         }
389         
390         // Disco info query
391         else if((iqQueryXMLNS == NS_DISCO_INFO) && (iqType == 'get')) {
392                 /* REF: http://xmpp.org/extensions/xep-0030.html */
393                 
394                 var iqQuery = iqResponse.setQuery(NS_DISCO_INFO);
395                 
396                 // We set the name of the client
397                 iqQuery.appendChild(iq.appendNode('identity', {
398                         'category': 'client',
399                         'type': 'web',
400                         'name': 'Jappix Mini',
401                         'xmlns': NS_DISCO_INFO
402                 }));
403                 
404                 // We set all the supported features
405                 var fArray = new Array(
406                         NS_DISCO_INFO,
407                         NS_VERSION,
408                         NS_ROSTER,
409                         NS_MUC,
410                         NS_VERSION,
411                         NS_URN_TIME
412                 );
413                 
414                 for(i in fArray)
415                         iqQuery.appendChild(iq.buildNode('feature', {'var': fArray[i], 'xmlns': NS_DISCO_INFO}));
416                 
417                 con.send(iqResponse);
418                 
419                 logThis('Received a disco#infos query.');
420         }
421         
422         // User time query
423         else if(jQuery(iqNode).find('time').size() && (iqType == 'get')) {
424                 /* REF: http://xmpp.org/extensions/xep-0202.html */
425                 
426                 var iqTime = iqResponse.appendNode('time', {'xmlns': NS_URN_TIME});
427                 iqTime.appendChild(iq.buildNode('tzo', {'xmlns': NS_URN_TIME}, getDateTZO()));
428                 iqTime.appendChild(iq.buildNode('utc', {'xmlns': NS_URN_TIME}, getXMPPTime('utc')));
429                 
430                 con.send(iqResponse);
431                 
432                 logThis('Received local time query: ' + iqFrom);
433         }
434 }
435
436 // Handles the incoming errors
437 function handleErrorMini(err) {
438         // First level error (connection error)
439         if(jQuery(err).is('error')) {
440                 // Notify this error
441                 disconnectedMini();
442                 
443                 logThis('First level error received.', 1);
444         }
445 }
446
447 // Handles the incoming presences
448 function handlePresenceMini(pr) {
449         // Get the values
450         var from = fullXID(getStanzaFrom(pr));
451         var xid = bareXID(from);
452         var resource = thisResource(from);
453         var hash = hex_md5(xid);
454         var type = pr.getType();
455         var show = pr.getShow();
456         
457         // Manage the received presence values
458         if((type == 'error') || (type == 'unavailable'))
459                 show = 'unavailable';
460         
461         else {
462                 switch(show) {
463                         case 'chat':
464                         case 'away':
465                         case 'xa':
466                         case 'dnd':
467                                 break;
468                         
469                         default:
470                                 show = 'available';
471                                 
472                                 break;
473                 }
474         }
475         
476         // Is this a groupchat presence?
477         var groupchat_path = '#jappix_mini #chat-' + hash + '[data-type=groupchat]';
478         
479         if(exists(groupchat_path)) {
480                 // Groupchat buddy presence (not me)
481                 if(resource != unescape(jQuery(groupchat_path).attr('data-nick'))) {
482                         // Regenerate some stuffs
483                         var groupchat = xid;
484                         xid = from;
485                         hash = hex_md5(xid);
486                         
487                         // Remove this from the roster
488                         if(show == 'unavailable')
489                                 removeBuddyMini(hash, groupchat);
490                         
491                         // Add this to the roster
492                         else
493                                 addBuddyMini(xid, hash, resource, groupchat);
494                 }
495         }
496         
497         // Friend path
498         var chat = '#jappix_mini #chat-' + hash;
499         var friend = '#jappix_mini a#friend-' + hash;
500         var send_input = chat + ' input.jm_send-messages';
501         
502         // Is this friend online?
503         if(show == 'unavailable') {
504                 // Offline marker
505                 jQuery(friend).addClass('jm_offline').removeClass('jm_online');
506                 
507                 // Disable the chat tools
508                 jQuery(chat).addClass('jm_disabled');
509                 jQuery(send_input).attr('disabled', true).attr('data-value', _e("Unavailable")).val(_e("Unavailable"));
510         }
511         
512         else {
513                 // Online marker
514                 jQuery(friend).removeClass('jm_offline').addClass('jm_online');
515                 
516                 // Enable the chat input
517                 jQuery(chat).removeClass('jm_disabled');
518                 jQuery(send_input).removeAttr('disabled').val('');
519         }
520         
521         // Change the show presence of this buddy
522         jQuery(friend + ' span.jm_presence, ' + chat + ' span.jm_presence').attr('class', 'jm_presence jm_images jm_' + show);
523         
524         // Update the presence counter
525         updateRosterMini();
526         
527         logThis('Presence received from: ' + from);
528 }
529
530 // Handles the MUC main elements
531 function handleMUCMini(pr) {
532         // We get the xml content
533         var xml = pr.getNode();
534         var from = fullXID(getStanzaFrom(pr));
535         var room = bareXID(from);
536         var hash = hex_md5(room);
537         var resource = thisResource(from);
538         
539         // Is it a valid server presence?
540         var valid = false;
541         
542         if(!resource || (resource == unescape(jQuery('#jappix_mini #chat-' + hash + '[data-type=groupchat]').attr('data-nick'))))
543                 valid = true;
544         
545         // Password required?
546         if(valid && jQuery(xml).find('error[type=auth] not-authorized').size()) {
547                 // Create a new prompt
548                 openPromptMini(printf(_e("This room (%s) is protected with a password."), room));
549                 
550                 // When prompt submitted
551                 jQuery('#jappix_popup div.jm_prompt form').submit(function() {
552                         try {
553                                 // Read the value
554                                 var password = closePromptMini();
555                                 
556                                 // Any submitted chat to join?
557                                 if(password) {
558                                         // Send the password
559                                         presenceMini('', '', '', '', from, password, true, handleMUCMini);
560                                         
561                                         // Focus on the pane again
562                                         switchPaneMini('chat-' + hash, hash);
563                                 }
564                         }
565                         
566                         catch(e) {}
567                         
568                         finally {
569                                 return false;
570                         }
571                 });
572                 
573                 return;
574         }
575         
576         // Nickname conflict?
577         else if(valid && jQuery(xml).find('error[type=cancel] conflict').size()) {
578                 // New nickname
579                 var nickname = resource + '_';
580                 
581                 // Send the new presence
582                 presenceMini('', '', '', '', room + '/' + nickname, '', true, handleMUCMini);
583                 
584                 // Update the nickname marker
585                 jQuery('#jappix_mini #chat-' + hash).attr('data-nick', escape(nickname));
586         }
587         
588         // Handle normal presence
589         else
590                 handlePresenceMini(pr);
591 }
592
593 // Updates the user presence
594 function presenceMini(type, show, priority, status, to, password, limit_history, handler) {
595         var pr = new JSJaCPresence();
596         
597         // Add the attributes
598         if(to)
599                 pr.setTo(to);
600         if(type)
601                 pr.setType(type);
602         if(show)
603                 pr.setShow(show);
604         if(priority)
605                 pr.setPriority(priority);
606         if(status)
607                 pr.setStatus(status);
608         
609         // Special presence elements
610         if(password || limit_history) {
611                 var x = pr.appendNode('x', {'xmlns': NS_MUC});
612                 
613                 // Any password?
614                 if(password)
615                         x.appendChild(pr.buildNode('password', {'xmlns': NS_MUC}, password));
616                 
617                 // Any history limit?
618                 if(limit_history)
619                         x.appendChild(pr.buildNode('history', {'maxstanzas': 10, 'seconds': 86400, 'xmlns': NS_MUC}));
620         }
621         
622         // Send the packet
623         if(handler)
624                 con.send(pr, handler);
625         else
626                 con.send(pr);
627         
628         // No type?
629         if(!type)
630                 type = 'available';
631         
632         logThis('Presence sent: ' + type, 3);
633 }
634
635 // Sends a given message
636 function sendMessageMini(aForm) {
637         try {
638                 var body = trim(aForm.body.value);
639                 var xid = aForm.xid.value;
640                 var type = aForm.type.value;
641                 var hash = hex_md5(xid);
642                 
643                 if(body && xid) {
644                         // Send the message
645                         var aMsg = new JSJaCMessage();
646                         
647                         aMsg.setTo(xid);
648                         aMsg.setType(type);
649                         aMsg.setBody(body);
650                         
651                         con.send(aMsg);
652                         
653                         // Clear the input
654                         aForm.body.value = '';
655                         
656                         // Display the message we sent
657                         if(type != 'groupchat')
658                                 displayMessageMini(type, body, getXID(), 'me', hash, getCompleteTime(), getTimeStamp(), 'user-message');
659                         
660                         logThis('Message (' + type + ') sent to: ' + xid);
661                 }
662         }
663         
664         catch(e) {}
665         
666         finally {
667                 return false;
668         }
669 }
670
671 // Generates the asked smiley image
672 function smileyMini(image, text) {
673         return ' <img class="jm_smiley jm_smiley-' + image + ' jm_images" alt="' + encodeQuotes(text) + '" src="' + JAPPIX_STATIC + 'php/get.php?t=img&amp;f=others/blank.gif" /> ';
674 }
675
676 // Notifies incoming chat messages
677 function notifyMessageMini(hash) {
678         // Define the paths
679         var tab = '#jappix_mini #chat-' + hash + ' a.jm_chat-tab';
680         var notify = tab + ' span.jm_notify';
681         var notify_middle = notify + ' span.jm_notify_middle';
682         
683         // Notification box not yet added
684         if(!exists(notify))
685                 jQuery(tab).append(
686                         '<span class="jm_notify">' + 
687                                 '<span class="jm_notify_left jm_images"></span>' + 
688                                 '<span class="jm_notify_middle">0</span>' + 
689                                 '<span class="jm_notify_right jm_images"></span>' + 
690                         '</span>'
691                 );
692         
693         // Increment the notification number
694         var number = parseInt(jQuery(notify_middle).text());
695         jQuery(notify_middle).text(number + 1);
696         
697         // Change the page title
698         notifyTitleMini();
699 }
700
701 // Notifies the user from a session error
702 function notifyErrorMini() {
703         // Replace the Jappix Mini DOM content
704         jQuery('#jappix_mini').html(
705                 '<div class="jm_starter">' + 
706                         '<a class="jm_pane jm_button jm_images" href="https://mini.jappix.com/issues" target="_blank" title="' + _e("Click here to solve the error") + '">' + 
707                                 '<span class="jm_counter jm_error jm_images">' + _e("Error") + '</span>' + 
708                         '</a>' + 
709                 '</div>'
710         );
711 }
712
713 // Updates the page title with the new notifications
714 function notifyTitleMini() {
715         // No saved title? Abort!
716         if(MINI_TITLE == null)
717                 return false;
718         
719         // Page title code
720         var title = MINI_TITLE;
721         
722         // Count the number of notifications
723         var number = 0;
724         
725         jQuery('#jappix_mini span.jm_notify span.jm_notify_middle').each(function() {
726                 number = number + parseInt(jQuery(this).text());
727         });
728         
729         // No new stuffs? Reset the title!
730         if(number)
731                 title = '[' + number + '] ' + title;
732         
733         // Apply the title
734         document.title = title;
735         
736         return true;
737 }
738
739 // Clears the notifications
740 function clearNotificationsMini(hash) {
741         // Not focused?
742         if(!isFocused())
743                 return false;
744         
745         // Remove the notifications counter
746         jQuery('#jappix_mini #chat-' + hash + ' span.jm_notify').remove();
747         
748         // Update the page title
749         notifyTitleMini();
750         
751         return true;
752 }
753
754 // Updates the roster counter
755 function updateRosterMini() {
756         jQuery('#jappix_mini a.jm_button span.jm_counter').text(jQuery('#jappix_mini a.jm_online').size());
757 }
758
759 // Creates the Jappix Mini DOM content
760 function createMini(domain, user, password) {
761         // Try to restore the DOM
762         var dom = getDB('jappix-mini', 'dom');
763         var stamp = parseInt(getDB('jappix-mini', 'stamp'));
764         var suspended = false;
765         
766         // Invalid stored DOM?
767         if(dom && isNaN(jQuery(dom).find('a.jm_pane.jm_button span.jm_counter').text()))
768                 dom = null;
769         
770         // Can resume a session?
771         con = new JSJaCHttpBindingConnection();
772         setupConMini(con);
773         
774         // Old DOM?
775         if(dom && ((getTimeStamp() - stamp) < JSJACHBC_MAX_WAIT) && con.resume()) {
776                 // Read the old nickname
777                 MINI_NICKNAME = getDB('jappix-mini', 'nickname');
778                 
779                 // Marker
780                 suspended = true;
781         }
782         
783         // New DOM?
784         else {
785                 dom =   '<div class="jm_position">' + 
786                                 '<div class="jm_conversations"></div>' + 
787                                 
788                                 '<div class="jm_starter">' + 
789                                         '<div class="jm_roster">' + 
790                                                 '<div class="jm_actions">' + 
791                                                         '<a class="jm_logo jm_images" href="https://mini.jappix.com/" target="_blank"></a>' + 
792                                                         '<a class="jm_one-action jm_join jm_images" title="' + _e("Join a chat") + '" href="#"></a>' + 
793                                                 '</div>' + 
794                                                 
795                                                 '<div class="jm_buddies"></div>' + 
796                                         '</div>' + 
797                                         
798                                         '<a class="jm_pane jm_button jm_images" href="#">' + 
799                                                 '<span class="jm_counter jm_images">' + _e("Please wait...") + '</span>' + 
800                                         '</a>' + 
801                                 '</div>' + 
802                         '</div>';
803         }
804         
805         // Create the DOM
806         jQuery('body').append('<div id="jappix_mini">' + dom + '</div>');
807         
808         // Adapt roster height
809         adaptRosterMini();
810         
811         // The click events
812         jQuery('#jappix_mini a.jm_button').click(function() {
813                 // Using a try/catch override IE issues
814                 try {
815                         // Presence counter
816                         var counter = '#jappix_mini a.jm_pane.jm_button span.jm_counter';
817                         
818                         // Cannot open the roster?
819                         if(jQuery(counter).text() == _e("Please wait..."))
820                                 return false;
821                         
822                         // Not yet connected?
823                         if(jQuery(counter).text() == _e("Chat")) {
824                                 // Remove the animated bubble
825                                 jQuery('#jappix_mini div.jm_starter span.jm_animate').stopTime().remove();
826                                 
827                                 // Add a waiting marker
828                                 jQuery(counter).text(_e("Please wait..."));
829                                 
830                                 // Launch the connection!
831                                 connectMini(domain, user, password);
832                                 
833                                 return false;
834                         }
835                         
836                         // Normal actions
837                         if(!jQuery(this).hasClass('jm_clicked'))
838                                 showRosterMini();
839                         else
840                                 hideRosterMini();
841                 }
842                 
843                 catch(e) {}
844                 
845                 finally {
846                         return false;
847                 }
848         });
849         
850         jQuery('#jappix_mini div.jm_actions a.jm_join').click(function() {
851                 // Using a try/catch override IE issues
852                 try {
853                         // Create a new prompt
854                         openPromptMini(_e("Please enter the group chat address to join."));
855                         
856                         // When prompt submitted
857                         jQuery('#jappix_popup div.jm_prompt form').submit(function() {
858                                 try {
859                                         // Read the value
860                                         var join_this = closePromptMini();
861                                         
862                                         // Any submitted chat to join?
863                                         if(join_this) {
864                                                 // Get the chat room to join
865                                                 chat_room = bareXID(generateXID(join_this, 'groupchat'));
866                                                 
867                                                 // Create a new groupchat
868                                                 chatMini('groupchat', chat_room, getXIDNick(chat_room), hex_md5(chat_room));
869                                         }
870                                 }
871                                 
872                                 catch(e) {}
873                                 
874                                 finally {
875                                         return false;
876                                 }
877                         });
878                 }
879                 
880                 catch(e) {}
881                 
882                 finally {
883                         return false;
884                 }
885         });
886         
887         // Hides the roster when clicking away of Jappix Mini
888         jQuery(document).click(function(evt) {
889                 if(!jQuery(evt.target).parents('#jappix_mini').size() && !exists('#jappix_popup'))
890                         hideRosterMini();
891         });
892         
893         // Hides all panes double clicking away of Jappix Mini
894         jQuery(document).dblclick(function(evt) {
895                 if(!jQuery(evt.target).parents('#jappix_mini').size() && !exists('#jappix_popup'))
896                         switchPaneMini();
897         });
898         
899         // Suspended session resumed?
900         if(suspended) {
901                 // Initialized marker
902                 MINI_INITIALIZED = true;
903                 
904                 // Restore chat input values
905                 jQuery('#jappix_mini div.jm_conversation input.jm_send-messages').each(function() {
906                         var chat_value = jQuery(this).attr('data-value');
907                         
908                         if(chat_value)
909                                 jQuery(this).val(chat_value);
910                 });
911                 
912                 // Restore buddy click events
913                 jQuery('#jappix_mini a.jm_friend').click(function() {
914                         // Using a try/catch override IE issues
915                         try {
916                                 chatMini('chat', unescape(jQuery(this).attr('data-xid')), unescape(jQuery(this).attr('data-nick')), jQuery(this).attr('data-hash'));
917                         }
918                         
919                         catch(e) {}
920                         
921                         finally {
922                                 return false;
923                         }
924                 });
925                 
926                 // Restore chat click events
927                 jQuery('#jappix_mini div.jm_conversation').each(function() {
928                         chatEventsMini(jQuery(this).attr('data-type'), unescape(jQuery(this).attr('data-xid')), jQuery(this).attr('data-hash'));
929                 });
930                 
931                 // Scroll down to the last message
932                 var scroll_hash = jQuery('#jappix_mini div.jm_conversation:has(a.jm_pane.jm_clicked)').attr('data-hash');
933                 var scroll_position = getDB('jappix-mini', 'scroll');
934                 
935                 // Any scroll position?
936                 if(scroll_position)
937                         scroll_position = parseInt(scroll_position);
938                 
939                 if(scroll_hash) {
940                         // Use a timer to override the DOM lag issue
941                         jQuery(document).oneTime(200, function() {
942                                 messageScrollMini(scroll_hash, scroll_position);
943                         });
944                 }
945                 
946                 // Update title notifications
947                 notifyTitleMini();
948         }
949         
950         // Can auto-connect?
951         else if(MINI_AUTOCONNECT)
952                 connectMini(domain, user, password);
953         
954         // Cannot auto-connect?
955         else {
956                 // Chat text
957                 jQuery('#jappix_mini a.jm_pane.jm_button span.jm_counter').text(_e("Chat"));
958                 
959                 // Must animate?
960                 if(MINI_ANIMATE) {
961                         // Add content
962                         jQuery('#jappix_mini div.jm_starter').prepend(
963                                 '<span class="jm_animate jm_images_animate"></span>'
964                         );
965                         
966                         // IE6 makes the image blink when animated...
967                         if((BrowserDetect.browser == 'Explorer') && (BrowserDetect.version < 7))
968                                 return;
969                         
970                         // Add timers
971                         var anim_i = 0;
972                         
973                         jQuery('#jappix_mini div.jm_starter span.jm_animate').everyTime(10, function() {
974                                 // Next
975                                 anim_i++;
976                                 
977                                 // Margins
978                                 var m_top = Math.cos(anim_i * 0.02) * 3;
979                                 var m_left = Math.sin(anim_i * 0.02) * 3;
980                                 
981                                 // Apply new position!
982                                 jQuery(this).css('margin-top', m_top + 'px')
983                                             .css('margin-left', m_left + 'px');
984                         });
985                 }
986         }
987 }
988
989 // Displays a given message
990 function displayMessageMini(type, body, xid, nick, hash, time, stamp, message_type) {
991         // Generate path
992         var path = '#chat-' + hash;
993         
994         // Can scroll?
995         var cont_scroll = document.getElementById('received-' + hash);
996         var can_scroll = false;
997         
998         if(!cont_scroll.scrollTop || ((cont_scroll.clientHeight + cont_scroll.scrollTop) == cont_scroll.scrollHeight))
999                 can_scroll = true;
1000         
1001         // Remove the previous message border if needed
1002         var last = jQuery(path + ' div.jm_group:last');
1003         var last_stamp = parseInt(last.attr('data-stamp'));
1004         var last_b = jQuery(path + ' b:last');
1005         var last_xid = last_b.attr('data-xid');
1006         var last_type = last.attr('data-type');
1007         var grouped = false;
1008         var header = '';
1009         
1010         if((last_xid == xid) && (message_type == last_type) && ((stamp - last_stamp) <= 1800))
1011                 grouped = true;
1012         
1013         else {
1014                 // Write the message date
1015                 if(nick)
1016                         header += '<span class="jm_date">' + time + '</span>';
1017                 
1018                 // Write the buddy name at the top of the message group
1019                 if(type == 'groupchat')
1020                         header += '<b style="color: ' + generateColor(nick) + ';" data-xid="' + encodeQuotes(xid) + '">' + nick.htmlEnc() + '</b>';
1021                 else if(nick == 'me')
1022                         header += '<b class="jm_me" data-xid="' + encodeQuotes(xid) + '">' + _e("You") + '</b>';
1023                 else
1024                         header += '<b class="jm_him" data-xid="' + encodeQuotes(xid) + '">' + nick.htmlEnc() + '</b>';
1025         }
1026         
1027         // Apply the /me command
1028         var me_command = false;
1029         
1030         if(body.match(/^\/me /i)) {
1031                 body = body.replace(/^\/me /i, nick + ' ');
1032                 
1033                 // Marker
1034                 me_command = true;
1035         }
1036         
1037         // HTML-encode the message
1038         body = body.htmlEnc();
1039         
1040         // Apply the smileys
1041         body = body.replace(/(;-?\))(\s|$)/gi, smileyMini('wink', '$1'))
1042                    .replace(/(:-?3)(\s|$)/gi, smileyMini('waii', '$1'))
1043                    .replace(/(:-?\()(\s|$)/gi, smileyMini('unhappy', '$1'))
1044                    .replace(/(:-?P)(\s|$)/gi, smileyMini('tongue', '$1'))
1045                    .replace(/(:-?O)(\s|$)/gi, smileyMini('surprised', '$1'))
1046                    .replace(/(:-?\))(\s|$)/gi, smileyMini('smile', '$1'))
1047                    .replace(/(\^_?\^)(\s|$)/gi, smileyMini('happy', '$1'))
1048                    .replace(/(:-?D)(\s|$)/gi, smileyMini('grin', '$1'));
1049         
1050         // Filter the links
1051         body = applyLinks(body, 'mini');
1052         
1053         // Generate the message code
1054         if(me_command)
1055                 body = '<em>' + body + '</em>';
1056         
1057         body = '<p>' + body + '</p>';
1058         
1059         // Create the message
1060         if(grouped)
1061                 jQuery('#jappix_mini #chat-' + hash + ' div.jm_received-messages div.jm_group:last').append(body);
1062         else
1063                 jQuery('#jappix_mini #chat-' + hash + ' div.jm_received-messages').append('<div class="jm_group jm_' + message_type + '" data-type="' + message_type + '" data-stamp="' + stamp + '">' + header + body + '</div>');
1064         
1065         // Scroll to this message
1066         if(can_scroll)
1067                 messageScrollMini(hash);
1068 }
1069
1070 // Switches to a given point
1071 function switchPaneMini(element, hash) {
1072         // Hide every item
1073         jQuery('#jappix_mini a.jm_pane').removeClass('jm_clicked');
1074         jQuery('#jappix_mini div.jm_roster, #jappix_mini div.jm_chat-content').hide();
1075         
1076         // Show the asked element
1077         if(element && (element != 'roster')) {
1078                 var current = '#jappix_mini #' + element;
1079                 
1080                 jQuery(current + ' a.jm_pane').addClass('jm_clicked');
1081                 jQuery(current + ' div.jm_chat-content').show();
1082                 
1083                 // Use a timer to override the DOM lag issue
1084                 jQuery(document).oneTime(10, function() {
1085                         jQuery(current + ' input.jm_send-messages').focus();
1086                 });
1087                 
1088                 // Scroll to the last message
1089                 if(hash)
1090                         messageScrollMini(hash);
1091         }
1092 }
1093
1094 // Scrolls to the last chat message
1095 function messageScrollMini(hash, position) {
1096         var id = document.getElementById('received-' + hash);
1097         
1098         // No defined position?
1099         if(!position)
1100                 position = id.scrollHeight;
1101         
1102         id.scrollTop = position;
1103 }
1104
1105 // Prompts the user with a given text
1106 function openPromptMini(text, value) {
1107         // Initialize
1108         var prompt = '#jappix_popup div.jm_prompt';
1109         var input = prompt + ' form input';
1110         var value_input = input + '[type=text]';
1111         
1112         // Remove the existing prompt
1113         closePromptMini();
1114         
1115         // Add the prompt
1116         jQuery('body').append(
1117                 '<div id="jappix_popup">' + 
1118                         '<div class="jm_prompt">' + 
1119                                 '<form>' + 
1120                                         text + 
1121                                         '<input class="jm_text" type="text" value="" />' + 
1122                                         '<input class="jm_submit" type="submit" value="' + _e("Submit") + '" />' + 
1123                                         '<input class="jm_submit" type="reset" value="' + _e("Cancel") + '" />' + 
1124                                         '<div class="jm_clear"></div>' + 
1125                                 '</form>' + 
1126                         '</div>' + 
1127                 '</div>'
1128         );
1129         
1130         // Vertical center
1131         var vert_pos = '-' + ((jQuery(prompt).height() / 2) + 10) + 'px';
1132         jQuery(prompt).css('margin-top', vert_pos);
1133         
1134         // Apply the value?
1135         if(value)
1136                 jQuery(value_input).val(value);
1137         
1138         // Focus on the input
1139         jQuery(document).oneTime(10, function() {
1140                 jQuery(value_input).focus();
1141         });
1142         
1143         // Cancel event
1144         jQuery(input + '[type=reset]').click(function() {
1145                 try {
1146                         closePromptMini();
1147                 }
1148                 
1149                 catch(e) {}
1150                 
1151                 finally {
1152                         return false;
1153                 }
1154         });
1155 }
1156
1157 // Returns the prompt value
1158 function closePromptMini() {
1159         // Read the value
1160         var value = jQuery('#jappix_popup div.jm_prompt form input').val();
1161         
1162         // Remove the popup
1163         jQuery('#jappix_popup').remove();
1164         
1165         return value;
1166 }
1167
1168 // Manages and creates a chat
1169 function chatMini(type, xid, nick, hash, pwd, show_pane) {
1170         var current = '#jappix_mini #chat-' + hash;
1171         
1172         // Not yet added?
1173         if(!exists(current)) {
1174                 // Groupchat nickname
1175                 if(type == 'groupchat') {
1176                         var nickname = MINI_NICKNAME;
1177                         
1178                         // No nickname?
1179                         if(!nickname) {
1180                                 // Create a new prompt
1181                                 openPromptMini(printf(_e("Please enter your nickname to join %s."), xid));
1182                                 
1183                                 // When prompt submitted
1184                                 jQuery('#jappix_popup div.jm_prompt form').submit(function() {
1185                                         try {
1186                                                 // Read the value
1187                                                 var nickname = closePromptMini();
1188                                                 
1189                                                 // Update the stored one
1190                                                 if(nickname)
1191                                                         MINI_NICKNAME = nickname;
1192                                                 
1193                                                 // Launch it again!
1194                                                 chatMini(type, xid, nick, hash, pwd);
1195                                         }
1196                                         
1197                                         catch(e) {}
1198                                         
1199                                         finally {
1200                                                 return false;
1201                                         }
1202                                 });
1203                                 
1204                                 return;
1205                         }
1206                 }
1207                 
1208                 // Create the HTML markup
1209                 var html = '<div class="jm_conversation jm_type_' + type + '" id="chat-' + hash + '" data-xid="' + escape(xid) + '" data-type="' + type + '" data-nick="' + escape(nick) + '" data-hash="' + hash + '" data-origin="' + escape(cutResource(xid)) + '">' + 
1210                                 '<div class="jm_chat-content">' + 
1211                                         '<div class="jm_actions">' + 
1212                                                 '<span class="jm_nick">' + nick + '</span>';
1213                 
1214                 // Check if the groupchat exists
1215                 var groupchat_exists = false;
1216                 
1217                 if(MINI_GROUPCHATS && MINI_GROUPCHATS.length) {
1218                         for(g in MINI_GROUPCHATS) {
1219                                 if(xid == bareXID(generateXID(MINI_GROUPCHATS[g], 'groupchat'))) {
1220                                         groupchat_exists = true;
1221                                         
1222                                         break;
1223                                 }
1224                         }
1225                 }
1226                 
1227                 // Any close button to display?
1228                 if(((type == 'groupchat') && !groupchat_exists) || (type != 'groupchat'))
1229                         html += '<a class="jm_one-action jm_close jm_images" title="' + _e("Close") + '" href="#"></a>';
1230                 
1231                 html += '</div>' + 
1232                         
1233                         '<div class="jm_received-messages" id="received-' + hash + '"></div>' + 
1234                                 '<form action="#" method="post">' + 
1235                                         '<input type="text" class="jm_send-messages" name="body" autocomplete="off" />' + 
1236                                         '<input type="hidden" name="xid" value="' + xid + '" />' + 
1237                                         '<input type="hidden" name="type" value="' + type + '" />' + 
1238                                 '</form>' + 
1239                         '</div>' + 
1240                         
1241                         '<a class="jm_pane jm_chat-tab jm_images" href="#">' + 
1242                                 '<span class="jm_name">' + nick.htmlEnc() + '</span>' + 
1243                         '</a>' + 
1244                 '</div>';
1245                 
1246                 jQuery('#jappix_mini div.jm_conversations').prepend(html);
1247                 
1248                 // Get the presence of this friend
1249                 if(type != 'groupchat') {
1250                         var selector = jQuery('#jappix_mini a#friend-' + hash + ' span.jm_presence');
1251                         
1252                         // Default presence
1253                         var show = 'available';
1254                         
1255                         // Read the presence
1256                         if(selector.hasClass('jm_unavailable'))
1257                                 show = 'unavailable';
1258                         else if(selector.hasClass('jm_chat'))
1259                                 show = 'chat';
1260                         else if(selector.hasClass('jm_away'))
1261                                 show = 'away';
1262                         else if(selector.hasClass('jm_xa'))
1263                                 show = 'xa';
1264                         else if(selector.hasClass('jm_dnd'))
1265                                 show = 'dnd';
1266                         
1267                         // Create the presence marker
1268                         jQuery(current + ' a.jm_chat-tab').prepend('<span class="jm_presence jm_images jm_' + show + '"></span>');
1269                 }
1270                 
1271                 // The click events
1272                 chatEventsMini(type, xid, hash);
1273                 
1274                 // Join the groupchat
1275                 if(type == 'groupchat') {
1276                         // Add the nickname value
1277                         jQuery(current).attr('data-nick', escape(nickname));
1278                         
1279                         // Send the first groupchat presence
1280                         presenceMini('', '', '', '', xid + '/' + nickname, pwd, true, handleMUCMini);
1281                 }
1282         }
1283         
1284         // Focus on our pane
1285         if(show_pane != false)
1286                 jQuery(document).oneTime(10, function() {
1287                         switchPaneMini('chat-' + hash, hash);
1288                 });
1289         
1290         return false;
1291 }
1292
1293 // Events on the chat tool
1294 function chatEventsMini(type, xid, hash) {
1295         var current = '#jappix_mini #chat-' + hash;
1296         
1297         // Submit the form
1298         jQuery(current + ' form').submit(function() {
1299                 return sendMessageMini(this);
1300         });
1301         
1302         // Click on the tab
1303         jQuery(current + ' a.jm_chat-tab').click(function() {
1304                 // Using a try/catch override IE issues
1305                 try {
1306                         // Not yet opened: open it!
1307                         if(!jQuery(this).hasClass('jm_clicked')) {
1308                                 // Show it!
1309                                 switchPaneMini('chat-' + hash, hash);
1310                                 
1311                                 // Clear the eventual notifications
1312                                 clearNotificationsMini(hash);
1313                         }
1314                         
1315                         // Yet opened: close it!
1316                         else
1317                                 switchPaneMini();
1318                 }
1319                 
1320                 catch(e) {}
1321                 
1322                 finally {
1323                         return false;
1324                 }
1325         });
1326         
1327         // Click on the close button
1328         jQuery(current + ' a.jm_close').click(function() {
1329                 // Using a try/catch override IE issues
1330                 try {
1331                         jQuery(current).remove();
1332                         
1333                         // Quit the groupchat?
1334                         if(type == 'groupchat') {
1335                                 // Send an unavailable presence
1336                                 presenceMini('unavailable', '', '', '', xid + '/' + unescape(jQuery(current).attr('data-nick')));
1337                                 
1338                                 // Remove this groupchat!
1339                                 removeGroupchatMini(xid);
1340                         }
1341                 }
1342                 
1343                 catch(e) {}
1344                 
1345                 finally {
1346                         return false;
1347                 }
1348         });
1349         
1350         // Click on the chat content
1351         jQuery(current + ' div.jm_received-messages').click(function() {
1352                 try {
1353                         jQuery(document).oneTime(10, function() {
1354                                 jQuery(current + ' input.jm_send-messages').focus();
1355                         });
1356                 }
1357                 
1358                 catch(e) {}
1359         });
1360         
1361         // Focus on the chat input
1362         jQuery(current + ' input.jm_send-messages').focus(function() {
1363                 clearNotificationsMini(hash);
1364         })
1365         
1366         // Change on the chat input
1367         .keyup(function() {
1368                 jQuery(this).attr('data-value', jQuery(this).val());
1369         });
1370 }
1371
1372 // Shows the roster
1373 function showRosterMini() {
1374         switchPaneMini('roster');
1375         jQuery('#jappix_mini div.jm_roster').show();
1376         jQuery('#jappix_mini a.jm_button').addClass('jm_clicked');
1377 }
1378
1379 // Hides the roster
1380 function hideRosterMini() {
1381         jQuery('#jappix_mini div.jm_roster').hide();
1382         jQuery('#jappix_mini a.jm_button').removeClass('jm_clicked');
1383 }
1384
1385 // Removes a groupchat from DOM
1386 function removeGroupchatMini(xid) {
1387         // Remove the groupchat private chats & the groupchat buddies from the roster
1388         jQuery('#jappix_mini div.jm_conversation[data-origin=' + escape(cutResource(xid)) + '], #jappix_mini div.jm_roster div.jm_grouped[data-xid=' + escape(xid) + ']').remove();
1389         
1390         // Update the presence counter
1391         updateRosterMini();
1392 }
1393
1394 // Initializes Jappix Mini
1395 function initializeMini() {
1396         // Update the marker
1397         MINI_INITIALIZED = true;
1398         
1399         // Send the initial presence
1400         if(!MINI_ANONYMOUS)
1401                 presenceMini();
1402         
1403         // Join the groupchats
1404         for(var i = 0; i < MINI_GROUPCHATS.length; i++) {
1405                 // Empty value?
1406                 if(!MINI_GROUPCHATS[i])
1407                         continue;
1408                 
1409                 // Using a try/catch override IE issues
1410                 try {
1411                         // Current chat room
1412                         var chat_room = bareXID(generateXID(MINI_GROUPCHATS[i], 'groupchat'));
1413                         
1414                         // Open the current chat
1415                         chatMini('groupchat', chat_room, getXIDNick(chat_room), hex_md5(chat_room), MINI_PASSWORDS[i], MINI_SHOWPANE);
1416                 }
1417                 
1418                 catch(e) {}
1419         }
1420         
1421         // Must show the roster?
1422         if(!MINI_AUTOCONNECT && !MINI_GROUPCHATS.length)
1423                 jQuery(document).oneTime(10, function() {
1424                         showRosterMini();
1425                 });
1426 }
1427
1428 // Displays a roster buddy
1429 function addBuddyMini(xid, hash, nick, groupchat) {
1430         // Element
1431         var element = '#jappix_mini a.jm_friend#friend-' + hash;
1432         
1433         // Yet added?
1434         if(exists(element))
1435                 return false;
1436         
1437         // Generate the path
1438         var path = '#jappix_mini div.jm_roster div.jm_buddies';
1439         
1440         // Groupchat buddy
1441         if(groupchat) {
1442                 // Generate the groupchat group path
1443                 path = '#jappix_mini div.jm_roster div.jm_grouped[data-xid=' + escape(groupchat) + ']';
1444                 
1445                 // Must add a groupchat group?
1446                 if(!exists(path)) {
1447                         jQuery('#jappix_mini div.jm_roster div.jm_buddies').append(
1448                                 '<div class="jm_grouped" data-xid="' + escape(groupchat) + '">' + 
1449                                         '<div class="jm_name">' + getXIDNick(groupchat).htmlEnc() + '</div>' + 
1450                                 '</div>'
1451                         );
1452                 }
1453         }
1454         
1455         // Append this buddy content
1456         var code = '<a class="jm_friend jm_offline" id="friend-' + hash + '" data-xid="' + escape(xid) + '" data-nick="' + escape(nick) +  '" data-hash="' + hash + '" href="#"><span class="jm_presence jm_images jm_unavailable"></span>' + nick.htmlEnc() + '</a>';
1457         
1458         if(groupchat)
1459                 jQuery(path).append(code);
1460         else
1461                 jQuery(path).prepend(code);
1462         
1463         // Click event on this buddy
1464         jQuery(element).click(function() {
1465                 // Using a try/catch override IE issues
1466                 try {
1467                         chatMini('chat', xid, nick, hash);
1468                 }
1469                 
1470                 catch(e) {}
1471                 
1472                 finally {
1473                         return false;
1474                 }
1475         });
1476         
1477         return true;
1478 }
1479
1480 // Removes a roster buddy
1481 function removeBuddyMini(hash, groupchat) {
1482         // Remove the buddy from the roster
1483         jQuery('#jappix_mini a.jm_friend#friend-' + hash).remove();
1484         
1485         // Empty group?
1486         var group = '#jappix_mini div.jm_roster div.jm_grouped[data-xid=' + escape(groupchat) + ']';
1487         
1488         if(groupchat && !jQuery(group + ' a.jm_friend').size())
1489                 jQuery(group).remove();
1490         
1491         return true;
1492 }
1493
1494 // Gets the user's roster
1495 function getRosterMini() {
1496         var iq = new JSJaCIQ();
1497         iq.setType('get');
1498         iq.setQuery(NS_ROSTER);
1499         con.send(iq, handleRosterMini);
1500         
1501         logThis('Getting roster...', 3);
1502 }
1503
1504 // Handles the user's roster
1505 function handleRosterMini(iq) {
1506         // Parse the roster
1507         jQuery(iq.getQuery()).find('item').each(function() {
1508                 // Get the values
1509                 var current = jQuery(this);
1510                 var xid = current.attr('jid');
1511                 var subscription = current.attr('subscription');
1512                 
1513                 // Not a gateway
1514                 if(!isGateway(xid)) {
1515                         var nick = current.attr('name');
1516                         var hash = hex_md5(xid);
1517                         
1518                         // No name is defined?
1519                         if(!nick)
1520                                 nick = getXIDNick(xid);
1521                         
1522                         // Action on the current buddy
1523                         if(subscription == 'remove')
1524                                 removeBuddyMini(hash);
1525                         else
1526                                 addBuddyMini(xid, hash, nick);
1527                 }
1528         });
1529         
1530         // Not yet initialized
1531         if(!MINI_INITIALIZED)
1532                 initializeMini();
1533         
1534         logThis('Roster got.', 3);
1535 }
1536
1537 // Adapts the roster height to the window
1538 function adaptRosterMini() {
1539         // Process the new height
1540         var height = jQuery(window).height() - 70;
1541         
1542         // Apply the new height
1543         jQuery('#jappix_mini div.jm_roster div.jm_buddies').css('max-height', height);
1544 }
1545
1546 // Plugin launcher
1547 function launchMini(autoconnect, show_pane, domain, user, password) {
1548         // Save infos to reconnect
1549         MINI_DOMAIN = domain;
1550         MINI_USER = user;
1551         MINI_PASSWORD = password;
1552         
1553         // Anonymous mode?
1554         if(!user || !password)
1555                 MINI_ANONYMOUS = true;
1556         else
1557                 MINI_ANONYMOUS = false;
1558         
1559         // Autoconnect (only if storage available to avoid floods)?
1560         if(autoconnect && hasDB())
1561                 MINI_AUTOCONNECT = true;
1562         else
1563                 MINI_AUTOCONNECT = false;
1564         
1565         // Show pane?
1566         if(show_pane)
1567                 MINI_SHOWPANE = true;
1568         else
1569                 MINI_SHOWPANE = false;
1570         
1571         // Remove Jappix Mini
1572         jQuery('#jappix_mini').remove();
1573         
1574         // Reconnect?
1575         if(MINI_RECONNECT) {
1576                 logThis('Trying to reconnect (try: ' + MINI_RECONNECT + ')!');
1577                 
1578                 return createMini(domain, user, password);
1579         }
1580         
1581         // Append the Mini stylesheet
1582         jQuery('head').append('<link rel="stylesheet" href="' + JAPPIX_STATIC + 'php/get.php?t=css&amp;g=mini.xml" type="text/css" media="all" />');
1583         
1584         // Legacy IE stylesheet
1585         if((BrowserDetect.browser == 'Explorer') && (BrowserDetect.version < 7))
1586                 jQuery('head').append('<link rel="stylesheet" href="' + JAPPIX_STATIC + 'php/get.php?t=css&amp;f=mini-ie.css" type="text/css" media="all" />');
1587         
1588         // Disables the browser HTTP-requests stopper
1589         jQuery(document).keydown(function(e) {
1590                 if((e.keyCode == 27) && !isDeveloper())
1591                         return false;
1592         });
1593         
1594         // Save the page title
1595         MINI_TITLE = document.title;
1596         
1597         // Sets the good roster max-height
1598         jQuery(window).resize(adaptRosterMini);
1599         
1600         // Logouts when Jappix is closed
1601         if(BrowserDetect.browser == 'Opera') {
1602                 // Emulates onbeforeunload on Opera (link clicked)
1603                 jQuery('a[href]:not([onclick])').click(function() {
1604                         // Link attributes
1605                         var href = jQuery(this).attr('href') || '';
1606                         var target = jQuery(this).attr('target') || '';
1607                         
1608                         // Not new window or JS link
1609                         if(href && !href.match(/^#/i) && !target.match(/_blank|_new/i))
1610                                 saveSessionMini();
1611                 });
1612                 
1613                 // Emulates onbeforeunload on Opera (form submitted)
1614                 jQuery('form:not([onsubmit])').submit(saveSessionMini);
1615         }
1616         
1617         jQuery(window).bind('beforeunload', saveSessionMini);
1618         
1619         // Create the Jappix Mini DOM content
1620         createMini(domain, user, password);
1621         
1622         logThis('Welcome to Jappix Mini! Happy coding in developer mode!');
1623 }