/* Jappix - An open social platform These are the chat JS scripts for Jappix ------------------------------------------------- License: AGPL Authors: Valérian Saliou, Eric, Maranda */ // Bundle var Chat = (function () { /** * Alias of this * @private */ var self = {}; /** * Apply generate events * @private * @param {string} path * @param {string} id * @param {string} xid * @return {undefined} */ self._generateEvents = function(path, id, xid) { try { // Click event: chat cleaner $(path + 'tools-clear').click(function() { self.clean(id); }); // Click event: call (audio) $(path + 'tools-jingle-audio').click(function() { Jingle.start(xid, 'audio'); }); // Click event: call (video) $(path + 'tools-jingle-video').click(function() { Jingle.start(xid, 'video'); }); // Click event: user-infos $(path + 'tools-infos').click(function() { UserInfos.open(xid); }); } catch(e) { Console.error('Chat._generateEvents', e); } }; /** * Apply generate events * @private * @param {object} input_sel * @param {string} xid * @param {string} hash * @return {undefined} */ self._createEvents = function(input_sel, xid, hash) { try { self._createEventsInput(input_sel, hash); self._createEventsKey(input_sel, xid, hash); self._createEventsMouse(xid, hash); } catch(e) { Console.error('Chat._createEvents', e); } }; /** * Apply generate events (input) * @private * @param {object} input_sel * @param {string} hash * @return {undefined} */ self._createEventsInput = function(input_sel, hash) { try { input_sel.focus(function() { // Clean notifications for this chat Interface.chanCleanNotify(hash); // Store focus on this chat! Interface.chat_focus_hash = hash; }); input_sel.blur(function() { // Reset storage about focus on this chat! if(Interface.chat_focus_hash == hash) { Interface.chat_focus_hash = null; } }); } catch(e) { Console.error('Chat._createEventsInput', e); } }; /** * Apply generate events (key) * @private * @param {object} input_sel * @param {string} xid * @param {string} hash * @return {undefined} */ self._createEventsKey = function(input_sel, xid, hash) { try { input_sel.keydown(function(e) { if(e.keyCode == 13) { // Enter key if(e.shiftKey || e.ctrlKey) { // Add a new line input_sel.val(input_sel.val() + '\n'); } else { if(Correction.isIn(xid) === true) { var corrected_value = input_sel.val().trim(); if(corrected_value) { // Send the corrected message Correction.send(xid, 'chat', corrected_value); } Correction.leave(xid); } else { // Send the message Message.send(hash, 'chat'); } // Reset the composing database entry DataStore.setDB(Connection.desktop_hash, 'chatstate', xid, 'off'); } return false; } else if(e.keyCode == 8) { // Leave correction mode? (another way, by flushing input value progressively) if(Correction.isIn(xid) === true && !input_sel.val()) { Correction.leave(xid); } } }); input_sel.keyup(function(e) { if(e.keyCode == 27) { // Escape key input_sel.val(''); // Leave correction mode? (simple escape way) if(Correction.isIn(xid) === true) { Correction.leave(xid); } } else { Correction.detect(xid, input_sel); } }); } catch(e) { Console.error('Chat._createEventsKey', e); } }; /** * Apply generate events (mouse) * @private * @param {string} xid * @param {string} hash * @return {undefined} */ self._createEventsMouse = function(xid, hash) { try { // Scroll in chat content $('#page-engine #' + hash + ' .content').scroll(function() { var self = this; if(Features.enabledMAM() && !(xid in MAM.map_pending)) { var has_state = xid in MAM.map_states; var rsm_count = has_state ? MAM.map_states[xid].rsm.count : 1; var rsm_before = has_state ? MAM.map_states[xid].rsm.first : ''; // Request more archives? if(rsm_count > 0 && $(this).scrollTop() < MAM.SCROLL_THRESHOLD) { var was_scroll_top = $(self).scrollTop() <= 32; var wait_mam = $('#' + hash).find('.wait-mam'); wait_mam.show(); MAM.getArchives({ 'with': xid }, { 'max': MAM.REQ_MAX, 'before': rsm_before }, function() { var wait_mam_height = was_scroll_top ? 0 : wait_mam.height(); wait_mam.hide(); // Restore scroll? if($(self).scrollTop() < MAM.SCROLL_THRESHOLD) { var sel_mam_chunk = $(self).find('.mam-chunk:first'); var cont_padding_top = parseInt($(self).css('padding-top').replace(/[^-\d\.]/g, '')); var cont_one_group_margin_bottom = parseInt(sel_mam_chunk.find('.one-group:last').css('margin-bottom').replace(/[^-\d\.]/g, '')); var cont_mam_chunk_height = sel_mam_chunk.height(); $(self).scrollTop(wait_mam_height + cont_padding_top + cont_one_group_margin_bottom + cont_mam_chunk_height); } }); } } }); } catch(e) { Console.error('Chat._createEventsMouse', e); } }; /** * Apply generate events * @private * @param {string} type * @param {string} id * @return {object} */ self._generateChatCode = function(type, id) { var code_args = {}; try { // Groupchat special code if(type == 'groupchat') { code_args.attributes = ' data-type="groupchat" data-correction="true"'; code_args.avatar = ''; code_args.name = '<p class="bc-infos"><b>' + Common._e("Subject") + '</b> <span class="muc-topic">' + Common._e("no subject defined for this room.") + '</span></p>'; code_args.code = '<div class="content groupchat-content" id="chat-content-' + id + '"></div>' + '<div class="list"><div class="moderator role"><p class="title">' + Common._e("Moderators") + '</p></div>' + '<div class="participant role"><p class="title">' + Common._e("Participants") + '</p></div>' + '<div class="visitor role"><p class="title">' + Common._e("Visitors") + '</p></div>' + '<div class="none role"><p class="title">' + Common._e("Others") + '</p></div></div>'; code_args.link = '<a href="#" class="tools-mucadmin tools-tooltip talk-images chat-tools-content" title="' + Common._e("Administration panel for this room") + '"></a>'; code_args.style = ''; // Is this a gateway? if(xid.match(/%/)) { code_args.disabled = ''; } else { code_args.disabled = ' disabled=""'; } } else { code_args.mam = '<div class="wait-mam wait-small"></div>'; code_args.attributes = ' data-type="chat"'; code_args.avatar = '<div class="avatar-container"><img class="avatar" src="' + './images/others/default-avatar.png' + '" alt="" /></div>'; code_args.name = '<div class="bc-pep"></div><p class="bc-infos"><span class="unavailable show talk-images"></span></p>'; code_args.code = '<div class="content" id="chat-content-' + id + '">' + code_args.mam + '</div>'; code_args.link = '<a href="#" class="tools-jingle-audio tools-tooltip talk-images chat-tools-content" title="' + Common._e("Call (audio only)") + '"></a>' + '<a href="#" class="tools-jingle-video tools-tooltip talk-images chat-tools-content" title="' + Common._e("Call (video)") + '"></a>' + '<a href="#" class="tools-infos tools-tooltip talk-images chat-tools-content" title="' + Common._e("Show user profile") + '"></a>'; code_args.style = ' style="display: none;"'; code_args.disabled = ''; } // Not a groupchat private chat, we can use the buddy add icon if((type == 'chat') || (type == 'groupchat')) { var title; if(type == 'chat') { title = Common._e("Add this contact to your friends"); } else { title = Common._e("Add this groupchat to your favorites"); } code_args.link += '<a href="#" class="tools-add tools-tooltip talk-images chat-tools-content" title="' + title + '"></a>'; } // IE DOM parsing bug fix code_args.style_picker = '<div class="chat-tools-content chat-tools-style"' + code_args.style + '>' + '<a href="#" class="tools-style tools-tooltip talk-images"></a>' + '</div>'; if((BrowserDetect.browser == 'Explorer') && (BrowserDetect.version < 9)) { code_args.style_picker = ''; } } catch(e) { Console.error('Chat._generateChatCode', e); } finally { return code_args; } }; /** * Correctly opens a new chat * @public * @param {string} xid * @param {string} type * @param {string} nickname * @param {string} password * @param {string} title * @return {boolean} */ self.checkCreate = function(xid, type, nickname, password, title) { try { // No XID? if(!xid) { return false; } // We generate some stuffs var hash = hex_md5(xid); var name; // Gets the name of the user/title of the room if(title) { name = title; } else { // Private groupchat chat if(type == 'private') { name = Common.thisResource(xid); } // XMPP-ID else if(xid.indexOf('@') != -1) { name = Name.getBuddy(xid); } // Gateway else { name = xid; } } // If the target div does not exist if(!Common.exists('#' + hash)) { // We check the type of the chat to open if((type == 'chat') || (type == 'private')) { self.create(hash, xid, name, type); } else if(type == 'groupchat') { // Try to read the room stored configuration if(!Utils.isAnonymous() && (!nickname || !password || !title)) { // Catch the room data var fData = $(Common.XMLFromString(DataStore.getDB(Connection.desktop_hash, 'favorites', xid))); var fNick = fData.find('nick').text(); var fPwd = fData.find('password').text(); var fName = fData.find('name').text(); // Apply the room data if(!nickname && fNick) nickname = fNick; if(!password && fPwd) password = fPwd; if(!title && fName) name = fName; } Groupchat.create(hash, xid, name, nickname, password); } } // Switch to the newly-created chat Interface.switchChan(hash); } catch(e) { Console.error('Chat.checkCreate', e); } finally { return false; } }; /** * Generates the chat DOM elements * @public * @param {string} type * @param {string} id * @param {string} xid * @param {string} nick * @return {undefined} */ self.generate = function(type, id, xid, nick) { try { // Generate some stuffs var path = '#' + id + ' .'; var escaped_xid = escape(xid); // Special code var chat_args = self._generateChatCode(type, id); // Append the chat HTML code $('#page-engine').append( '<div id="' + id + '" class="page-engine-chan chat one-counter"' + chat_args.attributes + ' data-xid="' + escaped_xid + '">' + '<div class="top ' + id + '">' + chat_args.avatar + '<div class="name">' + '<p class="bc-name bc-name-nick">' + nick.htmlEnc() + '</p>' + chat_args.name + '</div>' + '</div>' + chat_args.code + '<div class="text">' + '<div class="footer">' + '<div class="chat-tools-content chat-tools-smileys">' + '<a href="#" class="tools-smileys tools-tooltip talk-images"></a>' + '</div>' + chat_args.style_picker + '<div class="chat-tools-content chat-tools-file">' + '<a href="#" class="tools-file tools-tooltip talk-images"></a>' + '</div>' + '<div class="chat-tools-content chat-tools-save">' + '<a href="#" class="tools-save tools-tooltip talk-images"></a>' + '</div>' + '<a href="#" class="tools-clear tools-tooltip talk-images chat-tools-content" title="' + Common._e("Clean current chat") + '"></a>' + chat_args.link + '</div>' + '<div class="compose">' + '<textarea class="message-area focusable" ' + chat_args.disabled + ' data-to="' + escaped_xid + '" /></textarea>' + '</div>' + '</div>' + '</div>' ); self._generateEvents(path, id, xid); } catch(e) { Console.error('Chat.generate', e); } }; /** * Generates the chat switch elements * @public * @param {string} type * @param {string} id * @param {string} xid * @param {string} nick * @return {undefined} */ self.generateSwitch = function(type, id, xid, nick) { try { // Path to the element var chat_switch = '#page-switch .'; // Special code var special_class = ' unavailable'; var show_close = true; // Groupchat if(type == 'groupchat') { special_class = ' groupchat-default'; if(Utils.isAnonymous() && (xid == Common.generateXID(ANONYMOUS_ROOM, 'groupchat'))) { show_close = false; } } // Generate the HTML code var html = '<div class="' + id + ' switcher chan" onclick="return Interface.switchChan(\'' + Utils.encodeOnclick(id) + '\')">' + '<div class="icon talk-images' + special_class + '"></div>' + '<div class="name">' + nick.htmlEnc() + '</div>'; // Show the close button if not MUC and not anonymous if(show_close) { html += '<div class="exit" ' + 'title="' + Common._e("Close this tab") + '" ' + 'onclick="return Interface.quitThisChat(\'' + Utils.encodeOnclick(xid) + '\', \'' + Utils.encodeOnclick(id) + '\', \'' + Utils.encodeOnclick(type) + '\');">' + 'x' + '</div>'; } // Close the HTML html += '</div>'; // Append the HTML code $(chat_switch + 'chans, ' + chat_switch + 'more-content').append(html); } catch(e) { Console.error('Chat.generateSwitch', e); } }; /** * Cleans given the chat lines * @public * @param {string} chat * @return {undefined} */ self.clean = function(chat) { try { // Remove the messages $('#page-engine #' + chat + ' .content .one-group').remove(); // Clear the history database Message.removeLocalArchive(chat); // Focus again $(document).oneTime(10, function() { $('#page-engine #' + chat + ' .text .message-area').focus(); }); } catch(e) { Console.error('Chat.clean', e); } }; /** * Returns whether chat exists or not * @public * @param {string} hash * @return {boolean} */ self.exists = function(hash) { exists = false; try { if(hash) { exists = Common.exists('#' + hash + '.page-engine-chan[data-type="chat"]'); } } catch(e) { Console.error('Chat.exists', e); } finally { return exists; } }; /** * Creates a new chat * @public * @param {string} hash * @param {string} xid * @param {string} nick * @param {string} type * @return {undefined} */ self.create = function(hash, xid, nick, type) { try { Console.info('New chat: ' + xid); // Create the chat content self.generate(type, hash, xid, nick); // Create the chat switcher self.generateSwitch(type, hash, xid, nick); // Is this a chat? if(type == 'chat') { // MAM? Get archives from there! if(Features.enabledMAM()) { MAM.getArchives({ 'with': xid }, { 'max': MAM.REQ_MAX, 'before': '' }); } else { // Restore the chat history var chat_history = Message.readLocalArchive(hash); if(chat_history) { // Generate hashs var my_hash = hex_md5(Common.getXID()); var friend_hash = hex_md5(xid); // Add chat history HTML var path_sel = $('#' + hash); path_sel.find('.content').append(chat_history); // Filter old groups & messages var one_group_sel = path_sel.find('.one-group'); one_group_sel.filter('[data-type="user-message"]').addClass('from-history').attr('data-type', 'old-message'); path_sel.find('.user-message').removeClass('user-message').addClass('old-message'); // Regenerate user names one_group_sel.filter('.' + my_hash + ' b.name').text( Name.getBuddy(Common.getXID()) ); one_group_sel.filter('.' + friend_hash + ' b.name').text( Name.getBuddy(xid) ); // Regenerate group dates one_group_sel.each(function() { var current_stamp = parseInt($(this).attr('data-stamp'), 10); $(this).find('span.date').text(DateUtils.relative(current_stamp)); }); // Regenerate avatars if(Common.exists('#' + hash + ' .one-group.' + my_hash + ' .avatar-container')) { Avatar.get(Common.getXID(), 'cache', 'true', 'forget'); } if(Common.exists('#' + hash + ' .one-group.' + friend_hash + ' .avatar-container')) { Avatar.get(xid, 'cache', 'true', 'forget'); } } } // Add button if(!Roster.isFriend(xid)) { $('#' + hash + ' .tools-add').click(function() { // Hide the icon (to tell the user all is okay) $(this).hide(); // Send the subscribe request Roster.addThisContact(xid, nick); }).show(); } } // We catch the user's informations (like this avatar, vcard, and so on...) UserInfos.get(hash, xid, nick, type); // The icons-hover functions Tooltip.icons(xid, hash); // The event handlers var input_sel = $('#page-engine #' + hash + ' .message-area'); self._createEvents(input_sel, xid, hash); // Input events ChatState.events(input_sel, xid, hash, 'chat'); Markers.events(input_sel, xid, hash, 'chat'); } catch(e) { Console.error('Chat.create', e); } }; /** * Return class scope */ return self; })();