/* Jappix - An open social platform These are the dataform JS scripts for Jappix ------------------------------------------------- License: AGPL Author: Vanaryon Last revision: 28/08/11 */ // Gets the defined dataform elements function dataForm(host, type, node, action, target) { // Clean the current session cleanDataForm(target); // We tell the user that a search has been launched $('#' + target + ' .wait').show(); // If we have enough data if(host && type) { // Generate a session ID var sessionID = Math.round(100000.5 + (((900000.49999) - (100000.5)) * Math.random())); var id = target + '-' + sessionID + '-' + genID(); $('.' + target + '-results').attr('data-session', target + '-' + sessionID); // We request the service item var iq = new JSJaCIQ(); iq.setID(id); iq.setTo(host); iq.setType('get'); // MUC admin query if(type == 'muc') { iq.setQuery(NS_MUC_OWNER); con.send(iq, handleDataFormMuc); } // Browse query else if(type == 'browse') { var iqQuery = iq.setQuery(NS_DISCO_ITEMS); if(node) iqQuery.setAttribute('node', node); con.send(iq, handleDataFormBrowse); } // Command else if(type == 'command') { var items; if(node) items = iq.appendNode('command', {'node': node, 'xmlns': NS_COMMANDS}); else { items = iq.setQuery(NS_DISCO_ITEMS); items.setAttribute('node', NS_COMMANDS); } if(action && node) { iq.setType('set'); items.setAttribute('action', action); } con.send(iq, handleDataFormCommand); } // Search query else if(type == 'search') { iq.setQuery(NS_SEARCH); con.send(iq, handleDataFormSearch); } // Subscribe query else if(type == 'subscribe') { iq.setQuery(NS_REGISTER); con.send(iq, handleDataFormSubscribe); } // Join else if(type == 'join') { if(target == 'discovery') closeDiscovery(); checkChatCreate(host, 'groupchat'); } } return false; } // Sends a given dataform function sendDataForm(type, action, x_type, id, xid, node, sessionid, target) { // Path var pathID = '#' + target + ' .results[data-session=' + id + ']'; // New IQ var iq = new JSJaCIQ(); iq.setTo(xid); iq.setType('set'); // Set the correct query var query; if(type == 'subscribe') iqQuery = iq.setQuery(NS_REGISTER); else if(type == 'search') iqQuery = iq.setQuery(NS_SEARCH); else if(type == 'command') iqQuery = iq.appendNode('command', {'xmlns': NS_COMMANDS, 'node': node, 'sessionid': sessionid, 'action': action}); else if(type == 'x') iqQuery = iq.setQuery(NS_MUC_OWNER); // Build the XML document if(action != 'cancel') { // No X node if(exists('input.register-special') && (type == 'subscribe')) { $('input.register-special').each(function() { var iName = $(this).attr('name'); var iValue = $(this).val(); iqQuery.appendChild(iq.buildNode(iName, {'xmlns': NS_REGISTER}, iValue)); }); } // Can create the X node else { var iqX = iqQuery.appendChild(iq.buildNode('x', {'xmlns': NS_XDATA, 'type': x_type})); // Each input $(pathID + ' .oneresult input, ' + pathID + ' .oneresult textarea, ' + pathID + ' .oneresult select').each(function() { // Get the current input value var iVar = $(this).attr('name'); var iType = $(this).attr('data-type'); var iValue = $(this).val(); // Build a new field node var field = iqX.appendChild(iq.buildNode('field', {'var': iVar, 'type': iType, 'xmlns': NS_XDATA})); // Boolean input? if(iType == 'boolean') { if($(this).filter(':checked').size()) iValue = '1'; else iValue = '0'; } // JID-multi input? if(iType == 'jid-multi') { // Values array var xid_arr = [iValue]; var xid_check = []; // Try to split it if(iValue.indexOf(',') != -1) xid_arr = iValue.split(','); // Append each value to the XML document for(i in xid_arr) { // Get the current value xid_current = trim(xid_arr[i]); // No current value? if(!xid_current) continue; // Add the current value if(!existArrayValue(xid_check, xid_current)) { xid_check.push(xid_current); field.appendChild(iq.buildNode('value', {'xmlns': NS_XDATA}, xid_current)); } } } // List-multi selector? else if(iType == 'list-multi') { // Any value? if(iValue && iValue.length) { for(i in iValue) field.appendChild(iq.buildNode('value', {'xmlns': NS_XDATA}, iValue[i])); } } // Other inputs? else field.appendChild(iq.buildNode('value', {'xmlns': NS_XDATA}, iValue)); }); } } // Clean the current session cleanDataForm(target); // Show the waiting item $('#' + target + ' .wait').show(); // Change the ID of the current discovered item var iqID = target + '-' + genID(); $('#' + target + ' .' + target + '-results').attr('data-session', iqID); iq.setID(iqID); // Send the IQ if(type == 'subscribe') con.send(iq, handleDataFormSubscribe); else if(type == 'search') con.send(iq, handleDataFormSearch); else if(type == 'command') con.send(iq, handleDataFormCommand); else con.send(iq); return false; } // Displays the good dataform buttons function buttonsDataForm(type, action, id, xid, node, sessionid, target, pathID) { // No need to use buttons? if(type == 'muc') return; // Override the "undefined" output if(!id) id = ''; if(!xid) xid = ''; if(!node) node = ''; if(!sessionid) sessionid = ''; // We generate the buttons code var buttonsCode = '<div class="oneresult ' + target + '-oneresult ' + target + '-formtools">'; if(action == 'submit') { if((target == 'adhoc') && (type == 'command')) { buttonsCode += '<a href="#" class="submit" onclick="return sendDataForm(\'' + encodeOnclick(type) + '\', \'execute\', \'submit\', \'' + encodeOnclick(id) + '\', \'' + encodeOnclick(xid) + '\', \'' + encodeOnclick(node) + '\', \'' + encodeOnclick(sessionid) + '\', \'' + encodeOnclick(target) + '\');">' + _e("Submit") + '</a>'; // When keyup on one text input $(pathID + ' input').keyup(function(e) { if(e.keyCode == 13) { sendDataForm(type, 'execute', 'submit', id, xid, node, sessionid, target); return false; } }); } else { buttonsCode += '<a href="#" class="submit" onclick="return sendDataForm(\'' + encodeOnclick(type) + '\', \'submit\', \'submit\', \'' + encodeOnclick(id) + '\', \'' + encodeOnclick(xid) + '\', \'' + encodeOnclick(node) + '\', \'' + encodeOnclick(sessionid) + '\', \'' + encodeOnclick(target) + '\');">' + _e("Submit") + '</a>'; // When keyup on one text input $(pathID + ' input').keyup(function(e) { if(e.keyCode == 13) { sendDataForm(type, 'submit', 'submit', id, xid, node, sessionid, target); return false; } }); } } if((action == 'submit') && (type != 'subscribe') && (type != 'search')) buttonsCode += '<a href="#" class="submit" onclick="return sendDataForm(\'' + encodeOnclick(type) + '\', \'cancel\', \'cancel\', \'' + encodeOnclick(id) + '\', \'' + encodeOnclick(xid) + '\', \'' + encodeOnclick(node) + '\', \'' + encodeOnclick(sessionid) + '\', \'' + encodeOnclick(target) + '\');">' + _e("Cancel") + '</a>'; if(((action == 'back') || (type == 'subscribe') || (type == 'search')) && (target == 'discovery')) buttonsCode += '<a href="#" class="back" onclick="return startDiscovery();">' + _e("Close") + '</a>'; if((action == 'back') && ((target == 'welcome') || (target == 'directory'))) buttonsCode += '<a href="#" class="back" onclick="return dataForm(HOST_VJUD, \'search\', \'\', \'\', \'' + target + '\');">' + _e("Previous") + '</a>'; if((action == 'back') && (target == 'adhoc')) buttonsCode += '<a href="#" class="back" onclick="return dataForm(\'' + encodeOnclick(xid) + '\', \'command\', \'\', \'\', \'adhoc\');">' + _e("Previous") + '</a>'; buttonsCode += '</div>'; // We display the buttons code $(pathID).append(buttonsCode); // If no submit link, lock the form if(!exists(pathID + ' a.submit')) $(pathID + ' input, ' + pathID + ' textarea').attr('readonly', true); } // Handles the MUC dataform function handleDataFormMuc(iq) { handleErrorReply(iq); handleDataFormContent(iq, 'muc'); } // Handles the browse dataform function handleDataFormBrowse(iq) { handleErrorReply(iq); handleDataFormContent(iq, 'browse'); } // Handles the command dataform function handleDataFormCommand(iq) { handleErrorReply(iq); handleDataFormContent(iq, 'command'); } // Handles the subscribe dataform function handleDataFormSubscribe(iq) { handleErrorReply(iq); handleDataFormContent(iq, 'subscribe'); } // Handles the search dataform function handleDataFormSearch(iq) { handleErrorReply(iq); handleDataFormContent(iq, 'search'); } // Handles the dataform content function handleDataFormContent(iq, type) { // Get the ID var sID = iq.getID(); // Get the target var splitted = sID.split('-'); var target = splitted[0]; var sessionID = target + '-' + splitted[1]; var from = fullXID(getStanzaFrom(iq)); var pathID = '#' + target + ' .results[data-session=' + sessionID + ']'; // If an error occured if(!iq || (iq.getType() != 'result')) noResultDataForm(pathID); // If we got something okay else { var handleXML = iq.getNode(); if(type == 'browse') { if($(handleXML).find('item').attr('jid')) { // Get the query node var queryNode = $(handleXML).find('query').attr('node'); $(handleXML).find('item').each(function() { // We parse the received xml var itemHost = $(this).attr('jid'); var itemNode = $(this).attr('node'); var itemName = $(this).attr('name'); var itemHash = hex_md5(itemHost); // Node if(itemNode) $(pathID).append( '<div class="oneresult ' + target + '-oneresult" onclick="return dataForm(\'' + encodeOnclick(itemHost) + '\', \'browse\', \'' + encodeOnclick(itemNode) + '\', \'\', \'' + encodeOnclick(target) + '\');">' + '<div class="one-name">' + itemNode.htmlEnc() + '</div>' + '</div>' ); // Item else if(queryNode && itemName) $(pathID).append( '<div class="oneresult ' + target + '-oneresult">' + '<div class="one-name">' + itemName.htmlEnc() + '</div>' + '</div>' ); // Item with children else { // We display the waiting element $(pathID + ' .disco-wait .disco-category-title').after( '<div class="oneresult ' + target + '-oneresult ' + itemHash + '">' + '<div class="one-icon loading talk-images"></div>' + '<div class="one-host">' + itemHost + '</div>' + '<div class="one-type">' + _e("Requesting this service...") + '</div>' + '</div>' ); // We display the category $('#' + target + ' .disco-wait').show(); // We ask the server what's the service type getDataFormType(itemHost, itemNode, sessionID); } }); } // Else, there are no items for this query else noResultDataForm(pathID); } else if((type == 'muc') || (type == 'search') || (type == 'subscribe') || ((type == 'command') && $(handleXML).find('command').attr('xmlns'))) { // Get some values var xCommand = $(handleXML).find('command'); var bNode = xCommand.attr('node'); var bSession = xCommand.attr('sessionid'); var bStatus = xCommand.attr('status'); var xRegister = $(handleXML).find('query[xmlns=' + NS_REGISTER + ']').text(); var xElement = $(handleXML).find('x'); // Search done if((xElement.attr('type') == 'result') && (type == 'search')) { var bPath = pathID; // Display the result $(handleXML).find('item').each(function() { var bXID = $(this).find('field[var=jid] value:first').text(); var bName = $(this).find('field[var=fn] value:first').text(); var bCountry = $(this).find('field[var=ctry] value:first').text(); var dName = bName; // Override "undefined" value if(!bXID) bXID = ''; if(!bName) bName = _e("Unknown name"); if(!bCountry) bCountry = _e("Unknown country"); // User hash var bHash = hex_md5(bXID); // HTML code var bHTML = '<div class="oneresult ' + target + '-oneresult ' + bHash + '">' + '<div class="avatar-container">' + '<img class="avatar" src="' + './img/others/default-avatar.png' + '" alt="" />' + '</div>' + '<div class="one-fn">' + bName + '</div>' + '<div class="one-ctry">' + bCountry + '</div>' + '<div class="one-jid">' + bXID + '</div>' + '<div class="buttons-container">'; // The buddy is not in our buddy list? if(!exists('#buddy-list .buddy[data-xid=' + escape(bXID) + ']')) bHTML += '<a href="#" class="one-add one-vjud one-button talk-images">' + _e("Add") + '</a>'; // Chat button, if not in welcome/directory mode if(target == 'discovery') bHTML += '<a href="#" class="one-chat one-vjud one-button talk-images">' + _e("Chat") + '</a>'; // Close the HTML element bHTML += '</div></div>'; $(bPath).append(bHTML); // Click events $(bPath + ' .' + bHash + ' a').click(function() { // Buddy add if($(this).is('.one-add')) { $(this).hide(); addThisContact(bXID, dName); } // Buddy chat if($(this).is('.one-chat')) { if(target == 'discovery') closeDiscovery(); checkChatCreate(bXID , 'chat', '', '', dName); } return false; }); // Get the user's avatar if(bXID) getAvatar(bXID, 'cache', 'true', 'forget'); }); // No result? if(!$(handleXML).find('item').size()) noResultDataForm(pathID); // Previous button buttonsDataForm(type, 'back', sessionID, from, bNode, bSession, target, pathID); } // Command to complete else if(xElement.attr('xmlns') || ((type == 'subscribe') && xRegister)) { // We display the elements fillDataForm(handleXML, sessionID); // We display the buttons if(bStatus != 'completed') buttonsDataForm(type, 'submit', sessionID, from, bNode, bSession, target, pathID); else buttonsDataForm(type, 'back', sessionID, from, bNode, bSession, target, pathID); } // Command completed or subscription done else if(((bStatus == 'completed') && (type == 'command')) || (!xRegister && (type == 'subscribe'))) { // Display the good text var cNote = $(xCommand).find('note'); // Any note? if(cNote.size()) { cNote.each(function() { $(pathID).append( '<div class="onetitle ' + target + '-oneresult">' + $(this).text().htmlEnc() + '</div>' ); }); } // Default text else $(pathID).append('<div class="oneinstructions ' + target + '-oneresult">' + _e("Your form has been sent.") + '</div>'); // Display the back button buttonsDataForm(type, 'back', sessionID, from, '', '', target, pathID); // Add the gateway to our roster if subscribed if(type == 'subscribe') addThisContact(from); } // Command canceled else if((bStatus == 'canceled') && (type == 'command')) { if(target == 'discovery') startDiscovery(); else if(target == 'adhoc') dataForm(from, 'command', '', '', 'adhoc'); } // No items for this query else noResultDataForm(pathID); } else if(type == 'command') { if($(handleXML).find('item').attr('jid')) { // We display the elements $(handleXML).find('item').each(function() { // We parse the received xml var itemHost = $(this).attr('jid'); var itemNode = $(this).attr('node'); var itemName = $(this).attr('name'); var itemHash = hex_md5(itemHost); // We display the waiting element $(pathID).prepend( '<div class="oneresult ' + target + '-oneresult ' + itemHash + '" onclick="return dataForm(\'' + encodeOnclick(itemHost) + '\', \'command\', \'' + encodeOnclick(itemNode) + '\', \'execute\', \'' + encodeOnclick(target) + '\');">' + '<div class="one-name">' + itemName + '</div>' + '<div class="one-next">ยป</div>' + '</div>' ); }); } // Else, there are no items for this query else noResultDataForm(pathID); } } // Focus on the first input $(document).oneTime(10, function() { $(pathID + ' input:visible:first').focus(); }); // Hide the wait icon $('#' + target + ' .wait').hide(); } // Fills the dataform elements function fillDataForm(xml, id) { /* REF: http://xmpp.org/extensions/xep-0004.html */ // Initialize new vars var target = id.split('-')[0]; var pathID = '#' + target + ' .results[data-session=' + id + ']'; var selector, is_dataform; // Is it a dataform? if($(xml).find('x[xmlns=' + NS_XDATA + ']').size()) is_dataform = true; else is_dataform = false; // Determines the good selector to use if(is_dataform) selector = $(xml).find('x[xmlns=' + NS_XDATA + ']'); else selector = $(xml); // Form title selector.find('title').each(function() { $(pathID).append( '<div class="onetitle ' + target + '-oneresult">' + $(this).text().htmlEnc() + '</div>' ); }); // Form instructions selector.find('instructions').each(function() { $(pathID).append( '<div class="oneinstructions ' + target + '-oneresult">' + $(this).text().htmlEnc() + '</div>' ); }); // Register? if(!is_dataform) { // Items to detect var reg_names = [_e("Nickname"), _e("Name"), _e("Password"), _e("E-mail")]; var reg_ids = ['username', 'name', 'password', 'email']; // Append these inputs for(a in reg_names) { selector.find(reg_ids[a]).each(function() { $(pathID).append( '<div class="oneresult ' + target + '-oneresult">' + '<label>' + reg_names[a] + '</label>' + '<input name="' + reg_ids[a] + '" type="text" class="register-special dataform-i" />' + '</div>' ); }); } return false; } // Dataform? selector.find('field').each(function() { // We parse the received xml var type = $(this).attr('type'); var label = $(this).attr('label'); var field = $(this).attr('var'); var value = $(this).find('value:first').text(); var required = ''; // No value? if(!field) return; // Required input? if($(this).find('required').size()) required = ' required=""'; // Compatibility fix if(!label) label = field; if(!type) type = ''; // Generate some values var input; var hideThis = ''; // Fixed field if(type == 'fixed') $(pathID).append('<div class="oneinstructions">' + value.htmlEnc() + '</div>'); else { // Hidden field if(type == 'hidden') { hideThis = ' style="display: none;"'; input = '<input name="' + encodeQuotes(field) + '" data-type="' + encodeQuotes(type) + '" type="hidden" class="dataform-i" value="' + encodeQuotes(value) + '" ' + required + ' />'; } // Boolean field else if(type == 'boolean') { var checked; if(value == '1') checked = 'checked'; else checked = ''; input = '<input name="' + encodeQuotes(field) + '" type="checkbox" data-type="' + encodeQuotes(type) + '" class="dataform-i df-checkbox" ' + checked + required + ' />'; } // List-single/list-multi field else if((type == 'list-single') || (type == 'list-multi')) { var multiple = ''; // Multiple options? if(type == 'list-multi') multiple = ' multiple=""'; // Append the select field input = '<select name="' + encodeQuotes(field) + '" data-type="' + encodeQuotes(type) + '" class="dataform-i"' + required + multiple + '>'; var selected; // Append the available options $(this).find('option').each(function() { var nLabel = $(this).attr('label'); var nValue = $(this).find('value').text(); // No label? if(!nLabel) nLabel = nValue; // If this is the selected value if(nValue == value) selected = 'selected'; else selected = ''; input += '<option ' + selected + ' value="' + encodeQuotes(nValue) + '">' + nLabel.htmlEnc() + '</option>'; }); input += '</select>'; } // Text-multi field else if(type == 'text-multi') input = '<textarea rows="8" cols="60" data-type="' + encodeQuotes(type) + '" name="' + encodeQuotes(field) + '" class="dataform-i"' + required + '>' + value.htmlEnc() + '</textarea>'; // JID-multi field else if(type == 'jid-multi') { // Put the XID into an array var xid_arr = []; $(this).find('value').each(function() { var cValue = $(this).text(); if(!existArrayValue(xid_arr, cValue)) xid_arr.push(cValue); }); // Sort the array xid_arr.sort(); // Create the input var xid_value = ''; if(xid_arr.length) { for(i in xid_arr) { // Any pre-value if(xid_value) xid_value += ', '; // Add the current XID xid_value += xid_arr[i]; } } input = '<input name="' + encodeQuotes(field) + '" data-type="' + encodeQuotes(type) + '" type="text" class="dataform-i" value="' + encodeQuotes(xid_value) + '" placeholder="jack@jappix.com, jones@jappix.com"' + required + ' />'; } // Other stuffs that are similar else { // Text-single field var iType = 'text'; // Text-private field if(type == 'text-private') iType = 'password'; // JID-single field else if(type == 'jid-single') iType = 'email'; input = '<input name="' + encodeQuotes(field) + '" data-type="' + encodeQuotes(type) + '" type="' + iType + '" class="dataform-i" value="' + encodeQuotes(value) + '"' + required + ' />'; } // Append the HTML markup for this field $(pathID).append( '<div class="oneresult ' + target + '-oneresult"' + hideThis + '>' + '<label>' + label.htmlEnc() + '</label>' + input + '</div>' ); } }); return false; } // Gets the dataform type function getDataFormType(host, node, id) { var iq = new JSJaCIQ(); iq.setID(id + '-' + genID()); iq.setTo(host); iq.setType('get'); var iqQuery = iq.setQuery(NS_DISCO_INFO); if(node) iqQuery.setAttribute('node', node); con.send(iq, handleThisBrowse); } // Handles the browse stanza function handleThisBrowse(iq) { /* REF: http://xmpp.org/registrar/disco-categories.html */ var id = iq.getID(); var splitted = id.split('-'); var target = splitted[0]; var sessionID = target + '-' + splitted[1]; var from = fullXID(getStanzaFrom(iq)); var hash = hex_md5(from); var handleXML = iq.getQuery(); var pathID = '#' + target + ' .results[data-session=' + sessionID + ']'; // We first remove the waiting element $(pathID + ' .disco-wait .' + hash).remove(); if($(handleXML).find('identity').attr('type')) { var category = $(handleXML).find('identity').attr('category'); var type = $(handleXML).find('identity').attr('type'); var named = $(handleXML).find('identity').attr('name'); if(named) gName = named; else gName = ''; var one, two, three, four, five; // Get the features that this entity supports var findFeature = $(handleXML).find('feature'); for(i in findFeature) { var current = findFeature.eq(i).attr('var'); switch(current) { case NS_SEARCH: one = 1; break; case NS_MUC: two = 1; break; case NS_REGISTER: three = 1; break; case NS_COMMANDS: four = 1; break; case NS_DISCO_ITEMS: five = 1; break; default: break; } } var buttons = Array(one, two, three, four, five); // We define the toolbox links depending on the supported features var tools = ''; var aTools = Array('search', 'join', 'subscribe', 'command', 'browse'); var bTools = Array(_e("Search"), _e("Join"), _e("Subscribe"), _e("Command"), _e("Browse")); for(i in buttons) { if(buttons[i]) tools += '<a href="#" class="one-button ' + aTools[i] + ' talk-images" onclick="return dataForm(\'' + encodeOnclick(from) + '\', \'' + encodeOnclick(aTools[i]) + '\', \'\', \'\', \'' + encodeOnclick(target) + '\');" title="' + encodeOnclick(bTools[i]) + '"></a>'; } // As defined in the ref, we detect the type of each category to put an icon switch(category) { case 'account': case 'auth': case 'automation': case 'client': case 'collaboration': case 'component': case 'conference': case 'directory': case 'gateway': case 'headline': case 'hierarchy': case 'proxy': case 'pubsub': case 'server': case 'store': break; default: category = 'others'; } // We display the item we found $(pathID + ' .disco-' + category + ' .disco-category-title').after( '<div class="oneresult ' + target + '-oneresult ' + hash + '">' + '<div class="one-icon ' + category + ' talk-images"></div>' + '<div class="one-host">' + from + '</div>' + '<div class="one-type">' + gName + '</div>' + '<div class="one-actions">' + tools + '</div>' + '</div>' ); // We display the category $(pathID + ' .disco-' + category).show(); } else { $(pathID + ' .disco-others .disco-category-title').after( '<div class="oneresult ' + target + '-oneresult">' + '<div class="one-icon down talk-images"></div>' + '<div class="one-host">' + from + '</div>' + '<div class="one-type">' + _e("Service offline or broken") + '</div>' + '</div>' ); // We display the category $(pathID + ' .disco-others').show(); } // We hide the waiting stuffs if there's no remaining loading items if(!$(pathID + ' .disco-wait .' + target + '-oneresult').size()) $(pathID + ' .disco-wait, #' + target + ' .wait').hide(); } // Cleans the current data-form popup function cleanDataForm(target) { if(target == 'discovery') cleanDiscovery(); else $('#' + target + ' div.results').empty(); } // Displays the no result indicator function noResultDataForm(path) { $(path).prepend('<p class="no-results">' + _e("Sorry, but the entity didn't return any result!") + '</p>'); }