3 Jappix - An open social platform
4 These are the Jappix Mini JS scripts for Jappix
6 -------------------------------------------------
10 Last revision: 04/08/11
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;
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';
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);
40 // Connects the user with the given logins
41 function connectMini(domain, user, password) {
43 // We define the http binding parameters
47 oArgs.httpbase = HOST_BOSH_MINI;
49 oArgs.httpbase = HOST_BOSH;
51 // We create the new http-binding connection
52 con = new JSJaCHttpBindingConnection(oArgs);
54 // And we handle everything that happen
57 // Generate a resource
58 var random_resource = getDB('jappix-mini', 'resource');
61 random_resource = MINI_RESOURCE + ' (' + (new Date()).getTime() + ')';
63 // We retrieve what the user typed in the login inputs
66 oArgs.xmllang = XML_LANG;
67 oArgs.resource = random_resource;
68 oArgs.domain = domain;
70 // Store the resource (for reconnection)
71 setDB('jappix-mini', 'resource', random_resource);
75 // Anonymous mode disabled?
76 if(!allowedAnonymous()) {
77 logThis('Not allowed to use anonymous mode.', 2);
86 else if(lockHost() && (domain != HOST_ANONYMOUS)) {
87 logThis('Not allowed to connect to this anonymous domain: ' + domain, 2);
95 oArgs.authtype = 'saslanon';
101 if(lockHost() && (domain != HOST_MAIN)) {
102 logThis('Not allowed to connect to this main domain: ' + domain, 2);
112 MINI_NICKNAME = user;
114 oArgs.username = user;
115 oArgs.pass = password;
121 logThis('Jappix Mini is connecting...', 3);
126 logThis('Error while logging in: ' + e, 1);
137 // When the user is connected
138 function connectedMini() {
140 jQuery('#jappix_mini a.jm_pane.jm_button span.jm_counter').text('0');
142 // Do not get the roster if anonymous
150 logThis('Jappix Mini is now reconnected.', 3);
152 logThis('Jappix Mini is now connected.', 3);
154 // Reset reconnect var
158 // When the user disconnects
159 function saveSessionMini() {
164 // Save the actual Jappix Mini DOM
165 setDB('jappix-mini', 'dom', jQuery('#jappix_mini').html());
166 setDB('jappix-mini', 'nickname', MINI_NICKNAME);
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');
173 scroll_position = document.getElementById('received-' + scroll_hash).scrollTop + '';
175 setDB('jappix-mini', 'scroll', scroll_position);
177 // Save the session stamp
178 setDB('jappix-mini', 'stamp', getTimeStamp());
180 // Suspend connection
183 logThis('Jappix Mini session save tool launched.', 3);
186 // Disconnects the connected user
187 function disconnectMini() {
193 MINI_DISCONNECT = true;
194 MINI_INITIALIZED = false;
196 // Add disconnection handler
197 con.registerHandler('ondisconnect', disconnectedMini);
199 // Disconnect the user
202 logThis('Jappix Mini is disconnecting...', 3);
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');
216 if(!MINI_DISCONNECT || MINI_INITIALIZED) {
220 // Reset reconnect timer
221 jQuery('#jappix_mini').stopTime();
223 // Try to reconnect after a while
224 if(MINI_INITIALIZED && (MINI_RECONNECT < 5)) {
225 // Reconnect interval
226 var reconnect_interval = 10;
229 reconnect_interval = (5 + (5 * MINI_RECONNECT)) * 1000;
234 jQuery('#jappix_mini').oneTime(reconnect_interval, function() {
235 launchMini(true, MINI_SHOWPANE, MINI_DOMAIN, MINI_USER, MINI_PASSWORD);
240 // Normal disconnection?
242 launchMini(false, MINI_SHOWPANE, MINI_DOMAIN, MINI_USER, MINI_PASSWORD);
245 MINI_DISCONNECT = false;
246 MINI_INITIALIZED = false;
248 logThis('Jappix Mini is now disconnected.', 3);
251 // Handles the incoming messages
252 function handleMessageMini(msg) {
253 var type = msg.getType();
255 // This is a message Jappix can handle
256 if((type == 'chat') || (type == 'normal') || (type == 'groupchat') || !type) {
258 var body = trim(msg.getBody());
261 var subject = trim(msg.getSubject());
268 var from = fullXID(getStanzaFrom(msg));
269 var xid = bareXID(from);
271 var hash = hex_md5(xid);
272 var nick = thisResource(from);
275 var delay = readMessageDelay(msg.getNode());
280 time = relativeDate(delay);
281 d_stamp = Date.jab2date(delay);
285 time = getCompleteTime();
286 d_stamp = new Date();
290 var stamp = extractStamp(d_stamp);
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) {
300 // XID to use for a groupchat
306 var message_type = 'user-message';
309 if(type == 'groupchat') {
311 if(msg.getChild('delay', NS_URN_DELAY) || msg.getChild('x', NS_DELAY))
312 message_type = 'old-message';
315 if(!nick || subject) {
317 message_type = 'system-message';
323 nick = jQuery('#jappix_mini a#friend-' + hash).text().revertHtmlEnc();
327 nick = getXIDNick(xid);
330 // Define the target div
331 var target = '#jappix_mini #chat-' + hash;
333 // Create the chat if it does not exist
334 if(!exists(target) && (type != 'groupchat'))
335 chatMini(type, xid, nick, hash);
337 // Display the message
338 displayMessageMini(type, body, use_xid, nick, hash, time, stamp, message_type);
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);
344 logThis('Message received from: ' + from);
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();
358 // Build the response
359 var iqResponse = new JSJaCIQ();
361 iqResponse.setID(iqID);
362 iqResponse.setTo(iqFrom);
363 iqResponse.setType('result');
365 // Software version query
366 if((iqQueryXMLNS == NS_VERSION) && (iqType == 'get')) {
367 /* REF: http://xmpp.org/extensions/xep-0092.html */
369 var iqQuery = iqResponse.setQuery(NS_VERSION);
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));
375 con.send(iqResponse);
377 logThis('Received software version query: ' + iqFrom);
381 else if((iqQueryXMLNS == NS_ROSTER) && (iqType == 'set')) {
382 // Display the friend
383 handleRosterMini(iq);
385 con.send(iqResponse);
387 logThis('Received a roster push.');
391 else if((iqQueryXMLNS == NS_DISCO_INFO) && (iqType == 'get')) {
392 /* REF: http://xmpp.org/extensions/xep-0030.html */
394 var iqQuery = iqResponse.setQuery(NS_DISCO_INFO);
396 // We set the name of the client
397 iqQuery.appendChild(iq.appendNode('identity', {
398 'category': 'client',
400 'name': 'Jappix Mini',
401 'xmlns': NS_DISCO_INFO
404 // We set all the supported features
405 var fArray = new Array(
415 iqQuery.appendChild(iq.buildNode('feature', {'var': fArray[i], 'xmlns': NS_DISCO_INFO}));
417 con.send(iqResponse);
419 logThis('Received a disco#infos query.');
423 else if(jQuery(iqNode).find('time').size() && (iqType == 'get')) {
424 /* REF: http://xmpp.org/extensions/xep-0202.html */
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')));
430 con.send(iqResponse);
432 logThis('Received local time query: ' + iqFrom);
436 // Handles the incoming errors
437 function handleErrorMini(err) {
438 // First level error (connection error)
439 if(jQuery(err).is('error')) {
443 logThis('First level error received.', 1);
447 // Handles the incoming presences
448 function handlePresenceMini(pr) {
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();
457 // Manage the received presence values
458 if((type == 'error') || (type == 'unavailable'))
459 show = 'unavailable';
476 // Is this a groupchat presence?
477 var groupchat_path = '#jappix_mini #chat-' + hash + '[data-type=groupchat]';
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
487 // Remove this from the roster
488 if(show == 'unavailable')
489 removeBuddyMini(hash, groupchat);
491 // Add this to the roster
493 addBuddyMini(xid, hash, resource, groupchat);
498 var chat = '#jappix_mini #chat-' + hash;
499 var friend = '#jappix_mini a#friend-' + hash;
500 var send_input = chat + ' input.jm_send-messages';
502 // Is this friend online?
503 if(show == 'unavailable') {
505 jQuery(friend).addClass('jm_offline').removeClass('jm_online');
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"));
514 jQuery(friend).removeClass('jm_offline').addClass('jm_online');
516 // Enable the chat input
517 jQuery(chat).removeClass('jm_disabled');
518 jQuery(send_input).removeAttr('disabled').val('');
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);
524 // Update the presence counter
527 logThis('Presence received from: ' + from);
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);
539 // Is it a valid server presence?
542 if(!resource || (resource == unescape(jQuery('#jappix_mini #chat-' + hash + '[data-type=groupchat]').attr('data-nick'))))
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));
550 // When prompt submitted
551 jQuery('#jappix_popup div.jm_prompt form').submit(function() {
554 var password = closePromptMini();
556 // Any submitted chat to join?
559 presenceMini('', '', '', '', from, password, true, handleMUCMini);
561 // Focus on the pane again
562 switchPaneMini('chat-' + hash, hash);
576 // Nickname conflict?
577 else if(valid && jQuery(xml).find('error[type=cancel] conflict').size()) {
579 var nickname = resource + '_';
581 // Send the new presence
582 presenceMini('', '', '', '', room + '/' + nickname, '', true, handleMUCMini);
584 // Update the nickname marker
585 jQuery('#jappix_mini #chat-' + hash).attr('data-nick', escape(nickname));
588 // Handle normal presence
590 handlePresenceMini(pr);
593 // Updates the user presence
594 function presenceMini(type, show, priority, status, to, password, limit_history, handler) {
595 var pr = new JSJaCPresence();
597 // Add the attributes
605 pr.setPriority(priority);
607 pr.setStatus(status);
609 // Special presence elements
610 if(password || limit_history) {
611 var x = pr.appendNode('x', {'xmlns': NS_MUC});
615 x.appendChild(pr.buildNode('password', {'xmlns': NS_MUC}, password));
617 // Any history limit?
619 x.appendChild(pr.buildNode('history', {'maxstanzas': 10, 'seconds': 86400, 'xmlns': NS_MUC}));
624 con.send(pr, handler);
632 logThis('Presence sent: ' + type, 3);
635 // Sends a given message
636 function sendMessageMini(aForm) {
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);
645 var aMsg = new JSJaCMessage();
654 aForm.body.value = '';
656 // Display the message we sent
657 if(type != 'groupchat')
658 displayMessageMini(type, body, getXID(), 'me', hash, getCompleteTime(), getTimeStamp(), 'user-message');
660 logThis('Message (' + type + ') sent to: ' + xid);
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&f=others/blank.gif" /> ';
676 // Notifies incoming chat messages
677 function notifyMessageMini(hash) {
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';
683 // Notification box not yet added
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>' +
693 // Increment the notification number
694 var number = parseInt(jQuery(notify_middle).text());
695 jQuery(notify_middle).text(number + 1);
697 // Change the page title
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>' +
713 // Updates the page title with the new notifications
714 function notifyTitleMini() {
715 // No saved title? Abort!
716 if(MINI_TITLE == null)
720 var title = MINI_TITLE;
722 // Count the number of notifications
725 jQuery('#jappix_mini span.jm_notify span.jm_notify_middle').each(function() {
726 number = number + parseInt(jQuery(this).text());
729 // No new stuffs? Reset the title!
731 title = '[' + number + '] ' + title;
734 document.title = title;
739 // Clears the notifications
740 function clearNotificationsMini(hash) {
745 // Remove the notifications counter
746 jQuery('#jappix_mini #chat-' + hash + ' span.jm_notify').remove();
748 // Update the page title
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());
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;
766 // Invalid stored DOM?
767 if(dom && isNaN(jQuery(dom).find('a.jm_pane.jm_button span.jm_counter').text()))
770 // Can resume a session?
771 con = new JSJaCHttpBindingConnection();
775 if(dom && ((getTimeStamp() - stamp) < JSJACHBC_MAX_WAIT) && con.resume()) {
776 // Read the old nickname
777 MINI_NICKNAME = getDB('jappix-mini', 'nickname');
785 dom = '<div class="jm_position">' +
786 '<div class="jm_conversations"></div>' +
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>' +
795 '<div class="jm_buddies"></div>' +
798 '<a class="jm_pane jm_button jm_images" href="#">' +
799 '<span class="jm_counter jm_images">' + _e("Please wait...") + '</span>' +
806 jQuery('body').append('<div id="jappix_mini">' + dom + '</div>');
808 // Adapt roster height
812 jQuery('#jappix_mini a.jm_button').click(function() {
813 // Using a try/catch override IE issues
816 var counter = '#jappix_mini a.jm_pane.jm_button span.jm_counter';
818 // Cannot open the roster?
819 if(jQuery(counter).text() == _e("Please wait..."))
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();
827 // Add a waiting marker
828 jQuery(counter).text(_e("Please wait..."));
830 // Launch the connection!
831 connectMini(domain, user, password);
837 if(!jQuery(this).hasClass('jm_clicked'))
850 jQuery('#jappix_mini div.jm_actions a.jm_join').click(function() {
851 // Using a try/catch override IE issues
853 // Create a new prompt
854 openPromptMini(_e("Please enter the group chat address to join."));
856 // When prompt submitted
857 jQuery('#jappix_popup div.jm_prompt form').submit(function() {
860 var join_this = closePromptMini();
862 // Any submitted chat to join?
864 // Get the chat room to join
865 chat_room = bareXID(generateXID(join_this, 'groupchat'));
867 // Create a new groupchat
868 chatMini('groupchat', chat_room, getXIDNick(chat_room), hex_md5(chat_room));
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'))
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'))
899 // Suspended session resumed?
901 // Initialized marker
902 MINI_INITIALIZED = true;
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');
909 jQuery(this).val(chat_value);
912 // Restore buddy click events
913 jQuery('#jappix_mini a.jm_friend').click(function() {
914 // Using a try/catch override IE issues
916 chatMini('chat', unescape(jQuery(this).attr('data-xid')), unescape(jQuery(this).attr('data-nick')), jQuery(this).attr('data-hash'));
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'));
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');
935 // Any scroll position?
937 scroll_position = parseInt(scroll_position);
940 // Use a timer to override the DOM lag issue
941 jQuery(document).oneTime(200, function() {
942 messageScrollMini(scroll_hash, scroll_position);
946 // Update title notifications
951 else if(MINI_AUTOCONNECT)
952 connectMini(domain, user, password);
954 // Cannot auto-connect?
957 jQuery('#jappix_mini a.jm_pane.jm_button span.jm_counter').text(_e("Chat"));
962 jQuery('#jappix_mini div.jm_starter').prepend(
963 '<span class="jm_animate jm_images_animate"></span>'
966 // IE6 makes the image blink when animated...
967 if((BrowserDetect.browser == 'Explorer') && (BrowserDetect.version < 7))
973 jQuery('#jappix_mini div.jm_starter span.jm_animate').everyTime(10, function() {
978 var m_top = Math.cos(anim_i * 0.02) * 3;
979 var m_left = Math.sin(anim_i * 0.02) * 3;
981 // Apply new position!
982 jQuery(this).css('margin-top', m_top + 'px')
983 .css('margin-left', m_left + 'px');
989 // Displays a given message
990 function displayMessageMini(type, body, xid, nick, hash, time, stamp, message_type) {
992 var path = '#chat-' + hash;
995 var cont_scroll = document.getElementById('received-' + hash);
996 var can_scroll = false;
998 if(!cont_scroll.scrollTop || ((cont_scroll.clientHeight + cont_scroll.scrollTop) == cont_scroll.scrollHeight))
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;
1010 if((last_xid == xid) && (message_type == last_type) && ((stamp - last_stamp) <= 1800))
1014 // Write the message date
1016 header += '<span class="jm_date">' + time + '</span>';
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>';
1024 header += '<b class="jm_him" data-xid="' + encodeQuotes(xid) + '">' + nick.htmlEnc() + '</b>';
1027 // Apply the /me command
1028 var me_command = false;
1030 if(body.match(/^\/me /i)) {
1031 body = body.replace(/^\/me /i, nick + ' ');
1037 // HTML-encode the message
1038 body = body.htmlEnc();
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'));
1051 body = applyLinks(body, 'mini');
1053 // Generate the message code
1055 body = '<em>' + body + '</em>';
1057 body = '<p>' + body + '</p>';
1059 // Create the message
1061 jQuery('#jappix_mini #chat-' + hash + ' div.jm_received-messages div.jm_group:last').append(body);
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>');
1065 // Scroll to this message
1067 messageScrollMini(hash);
1070 // Switches to a given point
1071 function switchPaneMini(element, hash) {
1073 jQuery('#jappix_mini a.jm_pane').removeClass('jm_clicked');
1074 jQuery('#jappix_mini div.jm_roster, #jappix_mini div.jm_chat-content').hide();
1076 // Show the asked element
1077 if(element && (element != 'roster')) {
1078 var current = '#jappix_mini #' + element;
1080 jQuery(current + ' a.jm_pane').addClass('jm_clicked');
1081 jQuery(current + ' div.jm_chat-content').show();
1083 // Use a timer to override the DOM lag issue
1084 jQuery(document).oneTime(10, function() {
1085 jQuery(current + ' input.jm_send-messages').focus();
1088 // Scroll to the last message
1090 messageScrollMini(hash);
1094 // Scrolls to the last chat message
1095 function messageScrollMini(hash, position) {
1096 var id = document.getElementById('received-' + hash);
1098 // No defined position?
1100 position = id.scrollHeight;
1102 id.scrollTop = position;
1105 // Prompts the user with a given text
1106 function openPromptMini(text, value) {
1108 var prompt = '#jappix_popup div.jm_prompt';
1109 var input = prompt + ' form input';
1110 var value_input = input + '[type=text]';
1112 // Remove the existing prompt
1116 jQuery('body').append(
1117 '<div id="jappix_popup">' +
1118 '<div class="jm_prompt">' +
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>' +
1131 var vert_pos = '-' + ((jQuery(prompt).height() / 2) + 10) + 'px';
1132 jQuery(prompt).css('margin-top', vert_pos);
1136 jQuery(value_input).val(value);
1138 // Focus on the input
1139 jQuery(document).oneTime(10, function() {
1140 jQuery(value_input).focus();
1144 jQuery(input + '[type=reset]').click(function() {
1157 // Returns the prompt value
1158 function closePromptMini() {
1160 var value = jQuery('#jappix_popup div.jm_prompt form input').val();
1163 jQuery('#jappix_popup').remove();
1168 // Manages and creates a chat
1169 function chatMini(type, xid, nick, hash, pwd, show_pane) {
1170 var current = '#jappix_mini #chat-' + hash;
1173 if(!exists(current)) {
1174 // Groupchat nickname
1175 if(type == 'groupchat') {
1176 var nickname = MINI_NICKNAME;
1180 // Create a new prompt
1181 openPromptMini(printf(_e("Please enter your nickname to join %s."), xid));
1183 // When prompt submitted
1184 jQuery('#jappix_popup div.jm_prompt form').submit(function() {
1187 var nickname = closePromptMini();
1189 // Update the stored one
1191 MINI_NICKNAME = nickname;
1194 chatMini(type, xid, nick, hash, pwd);
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>';
1214 // Check if the groupchat exists
1215 var groupchat_exists = false;
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;
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>';
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 + '" />' +
1241 '<a class="jm_pane jm_chat-tab jm_images" href="#">' +
1242 '<span class="jm_name">' + nick.htmlEnc() + '</span>' +
1246 jQuery('#jappix_mini div.jm_conversations').prepend(html);
1248 // Get the presence of this friend
1249 if(type != 'groupchat') {
1250 var selector = jQuery('#jappix_mini a#friend-' + hash + ' span.jm_presence');
1253 var show = 'available';
1255 // Read the presence
1256 if(selector.hasClass('jm_unavailable'))
1257 show = 'unavailable';
1258 else if(selector.hasClass('jm_chat'))
1260 else if(selector.hasClass('jm_away'))
1262 else if(selector.hasClass('jm_xa'))
1264 else if(selector.hasClass('jm_dnd'))
1267 // Create the presence marker
1268 jQuery(current + ' a.jm_chat-tab').prepend('<span class="jm_presence jm_images jm_' + show + '"></span>');
1272 chatEventsMini(type, xid, hash);
1274 // Join the groupchat
1275 if(type == 'groupchat') {
1276 // Add the nickname value
1277 jQuery(current).attr('data-nick', escape(nickname));
1279 // Send the first groupchat presence
1280 presenceMini('', '', '', '', xid + '/' + nickname, pwd, true, handleMUCMini);
1284 // Focus on our pane
1285 if(show_pane != false)
1286 jQuery(document).oneTime(10, function() {
1287 switchPaneMini('chat-' + hash, hash);
1293 // Events on the chat tool
1294 function chatEventsMini(type, xid, hash) {
1295 var current = '#jappix_mini #chat-' + hash;
1298 jQuery(current + ' form').submit(function() {
1299 return sendMessageMini(this);
1303 jQuery(current + ' a.jm_chat-tab').click(function() {
1304 // Using a try/catch override IE issues
1306 // Not yet opened: open it!
1307 if(!jQuery(this).hasClass('jm_clicked')) {
1309 switchPaneMini('chat-' + hash, hash);
1311 // Clear the eventual notifications
1312 clearNotificationsMini(hash);
1315 // Yet opened: close it!
1327 // Click on the close button
1328 jQuery(current + ' a.jm_close').click(function() {
1329 // Using a try/catch override IE issues
1331 jQuery(current).remove();
1333 // Quit the groupchat?
1334 if(type == 'groupchat') {
1335 // Send an unavailable presence
1336 presenceMini('unavailable', '', '', '', xid + '/' + unescape(jQuery(current).attr('data-nick')));
1338 // Remove this groupchat!
1339 removeGroupchatMini(xid);
1350 // Click on the chat content
1351 jQuery(current + ' div.jm_received-messages').click(function() {
1353 jQuery(document).oneTime(10, function() {
1354 jQuery(current + ' input.jm_send-messages').focus();
1361 // Focus on the chat input
1362 jQuery(current + ' input.jm_send-messages').focus(function() {
1363 clearNotificationsMini(hash);
1366 // Change on the chat input
1368 jQuery(this).attr('data-value', jQuery(this).val());
1373 function showRosterMini() {
1374 switchPaneMini('roster');
1375 jQuery('#jappix_mini div.jm_roster').show();
1376 jQuery('#jappix_mini a.jm_button').addClass('jm_clicked');
1380 function hideRosterMini() {
1381 jQuery('#jappix_mini div.jm_roster').hide();
1382 jQuery('#jappix_mini a.jm_button').removeClass('jm_clicked');
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();
1390 // Update the presence counter
1394 // Initializes Jappix Mini
1395 function initializeMini() {
1396 // Update the marker
1397 MINI_INITIALIZED = true;
1399 // Send the initial presence
1403 // Join the groupchats
1404 for(var i = 0; i < MINI_GROUPCHATS.length; i++) {
1406 if(!MINI_GROUPCHATS[i])
1409 // Using a try/catch override IE issues
1411 // Current chat room
1412 var chat_room = bareXID(generateXID(MINI_GROUPCHATS[i], 'groupchat'));
1414 // Open the current chat
1415 chatMini('groupchat', chat_room, getXIDNick(chat_room), hex_md5(chat_room), MINI_PASSWORDS[i], MINI_SHOWPANE);
1421 // Must show the roster?
1422 if(!MINI_AUTOCONNECT && !MINI_GROUPCHATS.length)
1423 jQuery(document).oneTime(10, function() {
1428 // Displays a roster buddy
1429 function addBuddyMini(xid, hash, nick, groupchat) {
1431 var element = '#jappix_mini a.jm_friend#friend-' + hash;
1437 // Generate the path
1438 var path = '#jappix_mini div.jm_roster div.jm_buddies';
1442 // Generate the groupchat group path
1443 path = '#jappix_mini div.jm_roster div.jm_grouped[data-xid=' + escape(groupchat) + ']';
1445 // Must add a groupchat group?
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>' +
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>';
1459 jQuery(path).append(code);
1461 jQuery(path).prepend(code);
1463 // Click event on this buddy
1464 jQuery(element).click(function() {
1465 // Using a try/catch override IE issues
1467 chatMini('chat', xid, nick, hash);
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();
1486 var group = '#jappix_mini div.jm_roster div.jm_grouped[data-xid=' + escape(groupchat) + ']';
1488 if(groupchat && !jQuery(group + ' a.jm_friend').size())
1489 jQuery(group).remove();
1494 // Gets the user's roster
1495 function getRosterMini() {
1496 var iq = new JSJaCIQ();
1498 iq.setQuery(NS_ROSTER);
1499 con.send(iq, handleRosterMini);
1501 logThis('Getting roster...', 3);
1504 // Handles the user's roster
1505 function handleRosterMini(iq) {
1507 jQuery(iq.getQuery()).find('item').each(function() {
1509 var current = jQuery(this);
1510 var xid = current.attr('jid');
1511 var subscription = current.attr('subscription');
1514 if(!isGateway(xid)) {
1515 var nick = current.attr('name');
1516 var hash = hex_md5(xid);
1518 // No name is defined?
1520 nick = getXIDNick(xid);
1522 // Action on the current buddy
1523 if(subscription == 'remove')
1524 removeBuddyMini(hash);
1526 addBuddyMini(xid, hash, nick);
1530 // Not yet initialized
1531 if(!MINI_INITIALIZED)
1534 logThis('Roster got.', 3);
1537 // Adapts the roster height to the window
1538 function adaptRosterMini() {
1539 // Process the new height
1540 var height = jQuery(window).height() - 70;
1542 // Apply the new height
1543 jQuery('#jappix_mini div.jm_roster div.jm_buddies').css('max-height', height);
1547 function launchMini(autoconnect, show_pane, domain, user, password) {
1548 // Save infos to reconnect
1549 MINI_DOMAIN = domain;
1551 MINI_PASSWORD = password;
1554 if(!user || !password)
1555 MINI_ANONYMOUS = true;
1557 MINI_ANONYMOUS = false;
1559 // Autoconnect (only if storage available to avoid floods)?
1560 if(autoconnect && hasDB())
1561 MINI_AUTOCONNECT = true;
1563 MINI_AUTOCONNECT = false;
1567 MINI_SHOWPANE = true;
1569 MINI_SHOWPANE = false;
1571 // Remove Jappix Mini
1572 jQuery('#jappix_mini').remove();
1575 if(MINI_RECONNECT) {
1576 logThis('Trying to reconnect (try: ' + MINI_RECONNECT + ')!');
1578 return createMini(domain, user, password);
1581 // Append the Mini stylesheet
1582 jQuery('head').append('<link rel="stylesheet" href="' + JAPPIX_STATIC + 'php/get.php?t=css&g=mini.xml" type="text/css" media="all" />');
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&f=mini-ie.css" type="text/css" media="all" />');
1588 // Disables the browser HTTP-requests stopper
1589 jQuery(document).keydown(function(e) {
1590 if((e.keyCode == 27) && !isDeveloper())
1594 // Save the page title
1595 MINI_TITLE = document.title;
1597 // Sets the good roster max-height
1598 jQuery(window).resize(adaptRosterMini);
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() {
1605 var href = jQuery(this).attr('href') || '';
1606 var target = jQuery(this).attr('target') || '';
1608 // Not new window or JS link
1609 if(href && !href.match(/^#/i) && !target.match(/_blank|_new/i))
1613 // Emulates onbeforeunload on Opera (form submitted)
1614 jQuery('form:not([onsubmit])').submit(saveSessionMini);
1617 jQuery(window).bind('beforeunload', saveSessionMini);
1619 // Create the Jappix Mini DOM content
1620 createMini(domain, user, password);
1622 logThis('Welcome to Jappix Mini! Happy coding in developer mode!');