3 Jappix - An open social platform
4 These are the dataform JS scripts for Jappix
6 -------------------------------------------------
10 Last revision: 28/08/11
14 // Gets the defined dataform elements
15 function dataForm(host, type, node, action, target) {
16 // Clean the current session
17 cleanDataForm(target);
19 // We tell the user that a search has been launched
20 $('#' + target + ' .wait').show();
22 // If we have enough data
24 // Generate a session ID
25 var sessionID = Math.round(100000.5 + (((900000.49999) - (100000.5)) * Math.random()));
26 var id = target + '-' + sessionID + '-' + genID();
27 $('.' + target + '-results').attr('data-session', target + '-' + sessionID);
29 // We request the service item
30 var iq = new JSJaCIQ();
37 iq.setQuery(NS_MUC_OWNER);
38 con.send(iq, handleDataFormMuc);
42 else if(type == 'browse') {
43 var iqQuery = iq.setQuery(NS_DISCO_ITEMS);
46 iqQuery.setAttribute('node', node);
48 con.send(iq, handleDataFormBrowse);
52 else if(type == 'command') {
56 items = iq.appendNode('command', {'node': node, 'xmlns': NS_COMMANDS});
59 items = iq.setQuery(NS_DISCO_ITEMS);
60 items.setAttribute('node', NS_COMMANDS);
65 items.setAttribute('action', action);
68 con.send(iq, handleDataFormCommand);
72 else if(type == 'search') {
73 iq.setQuery(NS_SEARCH);
74 con.send(iq, handleDataFormSearch);
78 else if(type == 'subscribe') {
79 iq.setQuery(NS_REGISTER);
80 con.send(iq, handleDataFormSubscribe);
84 else if(type == 'join') {
85 if(target == 'discovery')
88 checkChatCreate(host, 'groupchat');
95 // Sends a given dataform
96 function sendDataForm(type, action, x_type, id, xid, node, sessionid, target) {
98 var pathID = '#' + target + ' .results[data-session=' + id + ']';
101 var iq = new JSJaCIQ();
105 // Set the correct query
108 if(type == 'subscribe')
109 iqQuery = iq.setQuery(NS_REGISTER);
110 else if(type == 'search')
111 iqQuery = iq.setQuery(NS_SEARCH);
112 else if(type == 'command')
113 iqQuery = iq.appendNode('command', {'xmlns': NS_COMMANDS, 'node': node, 'sessionid': sessionid, 'action': action});
115 iqQuery = iq.setQuery(NS_MUC_OWNER);
117 // Build the XML document
118 if(action != 'cancel') {
120 if(exists('input.register-special') && (type == 'subscribe')) {
121 $('input.register-special').each(function() {
122 var iName = $(this).attr('name');
123 var iValue = $(this).val();
125 iqQuery.appendChild(iq.buildNode(iName, {'xmlns': NS_REGISTER}, iValue));
129 // Can create the X node
131 var iqX = iqQuery.appendChild(iq.buildNode('x', {'xmlns': NS_XDATA, 'type': x_type}));
134 $(pathID + ' .oneresult input, ' + pathID + ' .oneresult textarea, ' + pathID + ' .oneresult select').each(function() {
135 // Get the current input value
136 var iVar = $(this).attr('name');
137 var iType = $(this).attr('data-type');
138 var iValue = $(this).val();
140 // Build a new field node
141 var field = iqX.appendChild(iq.buildNode('field', {'var': iVar, 'type': iType, 'xmlns': NS_XDATA}));
144 if(iType == 'boolean') {
145 if($(this).filter(':checked').size())
152 if(iType == 'jid-multi') {
154 var xid_arr = [iValue];
158 if(iValue.indexOf(',') != -1)
159 xid_arr = iValue.split(',');
161 // Append each value to the XML document
163 // Get the current value
164 xid_current = trim(xid_arr[i]);
170 // Add the current value
171 if(!existArrayValue(xid_check, xid_current)) {
172 xid_check.push(xid_current);
173 field.appendChild(iq.buildNode('value', {'xmlns': NS_XDATA}, xid_current));
178 // List-multi selector?
179 else if(iType == 'list-multi') {
181 if(iValue && iValue.length) {
183 field.appendChild(iq.buildNode('value', {'xmlns': NS_XDATA}, iValue[i]));
189 field.appendChild(iq.buildNode('value', {'xmlns': NS_XDATA}, iValue));
194 // Clean the current session
195 cleanDataForm(target);
197 // Show the waiting item
198 $('#' + target + ' .wait').show();
200 // Change the ID of the current discovered item
201 var iqID = target + '-' + genID();
202 $('#' + target + ' .' + target + '-results').attr('data-session', iqID);
206 if(type == 'subscribe')
207 con.send(iq, handleDataFormSubscribe);
208 else if(type == 'search')
209 con.send(iq, handleDataFormSearch);
210 else if(type == 'command')
211 con.send(iq, handleDataFormCommand);
218 // Displays the good dataform buttons
219 function buttonsDataForm(type, action, id, xid, node, sessionid, target, pathID) {
220 // No need to use buttons?
224 // Override the "undefined" output
234 // We generate the buttons code
235 var buttonsCode = '<div class="oneresult ' + target + '-oneresult ' + target + '-formtools">';
237 if(action == 'submit') {
238 if((target == 'adhoc') && (type == 'command')) {
239 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>';
241 // When keyup on one text input
242 $(pathID + ' input').keyup(function(e) {
243 if(e.keyCode == 13) {
244 sendDataForm(type, 'execute', 'submit', id, xid, node, sessionid, target);
252 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>';
254 // When keyup on one text input
255 $(pathID + ' input').keyup(function(e) {
256 if(e.keyCode == 13) {
257 sendDataForm(type, 'submit', 'submit', id, xid, node, sessionid, target);
265 if((action == 'submit') && (type != 'subscribe') && (type != 'search'))
266 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>';
268 if(((action == 'back') || (type == 'subscribe') || (type == 'search')) && (target == 'discovery'))
269 buttonsCode += '<a href="#" class="back" onclick="return startDiscovery();">' + _e("Close") + '</a>';
271 if((action == 'back') && ((target == 'welcome') || (target == 'directory')))
272 buttonsCode += '<a href="#" class="back" onclick="return dataForm(HOST_VJUD, \'search\', \'\', \'\', \'' + target + '\');">' + _e("Previous") + '</a>';
274 if((action == 'back') && (target == 'adhoc'))
275 buttonsCode += '<a href="#" class="back" onclick="return dataForm(\'' + encodeOnclick(xid) + '\', \'command\', \'\', \'\', \'adhoc\');">' + _e("Previous") + '</a>';
277 buttonsCode += '</div>';
279 // We display the buttons code
280 $(pathID).append(buttonsCode);
282 // If no submit link, lock the form
283 if(!exists(pathID + ' a.submit'))
284 $(pathID + ' input, ' + pathID + ' textarea').attr('readonly', true);
287 // Handles the MUC dataform
288 function handleDataFormMuc(iq) {
289 handleErrorReply(iq);
290 handleDataFormContent(iq, 'muc');
293 // Handles the browse dataform
294 function handleDataFormBrowse(iq) {
295 handleErrorReply(iq);
296 handleDataFormContent(iq, 'browse');
299 // Handles the command dataform
300 function handleDataFormCommand(iq) {
301 handleErrorReply(iq);
302 handleDataFormContent(iq, 'command');
305 // Handles the subscribe dataform
306 function handleDataFormSubscribe(iq) {
307 handleErrorReply(iq);
308 handleDataFormContent(iq, 'subscribe');
311 // Handles the search dataform
312 function handleDataFormSearch(iq) {
313 handleErrorReply(iq);
314 handleDataFormContent(iq, 'search');
317 // Handles the dataform content
318 function handleDataFormContent(iq, type) {
320 var sID = iq.getID();
323 var splitted = sID.split('-');
324 var target = splitted[0];
325 var sessionID = target + '-' + splitted[1];
326 var from = fullXID(getStanzaFrom(iq));
327 var pathID = '#' + target + ' .results[data-session=' + sessionID + ']';
329 // If an error occured
330 if(!iq || (iq.getType() != 'result'))
331 noResultDataForm(pathID);
333 // If we got something okay
335 var handleXML = iq.getNode();
337 if(type == 'browse') {
338 if($(handleXML).find('item').attr('jid')) {
339 // Get the query node
340 var queryNode = $(handleXML).find('query').attr('node');
342 $(handleXML).find('item').each(function() {
343 // We parse the received xml
344 var itemHost = $(this).attr('jid');
345 var itemNode = $(this).attr('node');
346 var itemName = $(this).attr('name');
347 var itemHash = hex_md5(itemHost);
352 '<div class="oneresult ' + target + '-oneresult" onclick="return dataForm(\'' + encodeOnclick(itemHost) + '\', \'browse\', \'' + encodeOnclick(itemNode) + '\', \'\', \'' + encodeOnclick(target) + '\');">' +
353 '<div class="one-name">' + itemNode.htmlEnc() + '</div>' +
358 else if(queryNode && itemName)
360 '<div class="oneresult ' + target + '-oneresult">' +
361 '<div class="one-name">' + itemName.htmlEnc() + '</div>' +
365 // Item with children
367 // We display the waiting element
368 $(pathID + ' .disco-wait .disco-category-title').after(
369 '<div class="oneresult ' + target + '-oneresult ' + itemHash + '">' +
370 '<div class="one-icon loading talk-images"></div>' +
371 '<div class="one-host">' + itemHost + '</div>' +
372 '<div class="one-type">' + _e("Requesting this service...") + '</div>' +
376 // We display the category
377 $('#' + target + ' .disco-wait').show();
379 // We ask the server what's the service type
380 getDataFormType(itemHost, itemNode, sessionID);
385 // Else, there are no items for this query
387 noResultDataForm(pathID);
390 else if((type == 'muc') || (type == 'search') || (type == 'subscribe') || ((type == 'command') && $(handleXML).find('command').attr('xmlns'))) {
392 var xCommand = $(handleXML).find('command');
393 var bNode = xCommand.attr('node');
394 var bSession = xCommand.attr('sessionid');
395 var bStatus = xCommand.attr('status');
396 var xRegister = $(handleXML).find('query[xmlns=' + NS_REGISTER + ']').text();
397 var xElement = $(handleXML).find('x');
400 if((xElement.attr('type') == 'result') && (type == 'search')) {
403 // Display the result
404 $(handleXML).find('item').each(function() {
405 var bXID = $(this).find('field[var=jid] value:first').text();
406 var bName = $(this).find('field[var=fn] value:first').text();
407 var bCountry = $(this).find('field[var=ctry] value:first').text();
410 // Override "undefined" value
414 bName = _e("Unknown name");
416 bCountry = _e("Unknown country");
419 var bHash = hex_md5(bXID);
422 var bHTML = '<div class="oneresult ' + target + '-oneresult ' + bHash + '">' +
423 '<div class="avatar-container">' +
424 '<img class="avatar" src="' + './img/others/default-avatar.png' + '" alt="" />' +
426 '<div class="one-fn">' + bName + '</div>' +
427 '<div class="one-ctry">' + bCountry + '</div>' +
428 '<div class="one-jid">' + bXID + '</div>' +
429 '<div class="buttons-container">';
431 // The buddy is not in our buddy list?
432 if(!exists('#buddy-list .buddy[data-xid=' + escape(bXID) + ']'))
433 bHTML += '<a href="#" class="one-add one-vjud one-button talk-images">' + _e("Add") + '</a>';
435 // Chat button, if not in welcome/directory mode
436 if(target == 'discovery')
437 bHTML += '<a href="#" class="one-chat one-vjud one-button talk-images">' + _e("Chat") + '</a>';
439 // Close the HTML element
440 bHTML += '</div></div>';
442 $(bPath).append(bHTML);
445 $(bPath + ' .' + bHash + ' a').click(function() {
447 if($(this).is('.one-add')) {
450 addThisContact(bXID, dName);
454 if($(this).is('.one-chat')) {
455 if(target == 'discovery')
458 checkChatCreate(bXID , 'chat', '', '', dName);
464 // Get the user's avatar
466 getAvatar(bXID, 'cache', 'true', 'forget');
470 if(!$(handleXML).find('item').size())
471 noResultDataForm(pathID);
474 buttonsDataForm(type, 'back', sessionID, from, bNode, bSession, target, pathID);
477 // Command to complete
478 else if(xElement.attr('xmlns') || ((type == 'subscribe') && xRegister)) {
479 // We display the elements
480 fillDataForm(handleXML, sessionID);
482 // We display the buttons
483 if(bStatus != 'completed')
484 buttonsDataForm(type, 'submit', sessionID, from, bNode, bSession, target, pathID);
486 buttonsDataForm(type, 'back', sessionID, from, bNode, bSession, target, pathID);
489 // Command completed or subscription done
490 else if(((bStatus == 'completed') && (type == 'command')) || (!xRegister && (type == 'subscribe'))) {
491 // Display the good text
492 var cNote = $(xCommand).find('note');
496 cNote.each(function() {
498 '<div class="onetitle ' + target + '-oneresult">' + $(this).text().htmlEnc() + '</div>'
505 $(pathID).append('<div class="oneinstructions ' + target + '-oneresult">' + _e("Your form has been sent.") + '</div>');
507 // Display the back button
508 buttonsDataForm(type, 'back', sessionID, from, '', '', target, pathID);
510 // Add the gateway to our roster if subscribed
511 if(type == 'subscribe')
512 addThisContact(from);
516 else if((bStatus == 'canceled') && (type == 'command')) {
517 if(target == 'discovery')
519 else if(target == 'adhoc')
520 dataForm(from, 'command', '', '', 'adhoc');
523 // No items for this query
525 noResultDataForm(pathID);
528 else if(type == 'command') {
529 if($(handleXML).find('item').attr('jid')) {
530 // We display the elements
531 $(handleXML).find('item').each(function() {
532 // We parse the received xml
533 var itemHost = $(this).attr('jid');
534 var itemNode = $(this).attr('node');
535 var itemName = $(this).attr('name');
536 var itemHash = hex_md5(itemHost);
538 // We display the waiting element
540 '<div class="oneresult ' + target + '-oneresult ' + itemHash + '" onclick="return dataForm(\'' + encodeOnclick(itemHost) + '\', \'command\', \'' + encodeOnclick(itemNode) + '\', \'execute\', \'' + encodeOnclick(target) + '\');">' +
541 '<div class="one-name">' + itemName + '</div>' +
542 '<div class="one-next">»</div>' +
548 // Else, there are no items for this query
550 noResultDataForm(pathID);
554 // Focus on the first input
555 $(document).oneTime(10, function() {
556 $(pathID + ' input:visible:first').focus();
559 // Hide the wait icon
560 $('#' + target + ' .wait').hide();
563 // Fills the dataform elements
564 function fillDataForm(xml, id) {
565 /* REF: http://xmpp.org/extensions/xep-0004.html */
567 // Initialize new vars
568 var target = id.split('-')[0];
569 var pathID = '#' + target + ' .results[data-session=' + id + ']';
570 var selector, is_dataform;
573 if($(xml).find('x[xmlns=' + NS_XDATA + ']').size())
578 // Determines the good selector to use
580 selector = $(xml).find('x[xmlns=' + NS_XDATA + ']');
585 selector.find('title').each(function() {
587 '<div class="onetitle ' + target + '-oneresult">' + $(this).text().htmlEnc() + '</div>'
592 selector.find('instructions').each(function() {
594 '<div class="oneinstructions ' + target + '-oneresult">' + $(this).text().htmlEnc() + '</div>'
601 var reg_names = [_e("Nickname"), _e("Name"), _e("Password"), _e("E-mail")];
602 var reg_ids = ['username', 'name', 'password', 'email'];
604 // Append these inputs
605 for(a in reg_names) {
606 selector.find(reg_ids[a]).each(function() {
608 '<div class="oneresult ' + target + '-oneresult">' +
609 '<label>' + reg_names[a] + '</label>' +
610 '<input name="' + reg_ids[a] + '" type="text" class="register-special dataform-i" />' +
620 selector.find('field').each(function() {
621 // We parse the received xml
622 var type = $(this).attr('type');
623 var label = $(this).attr('label');
624 var field = $(this).attr('var');
625 var value = $(this).find('value:first').text();
633 if($(this).find('required').size())
634 required = ' required=""';
643 // Generate some values
649 $(pathID).append('<div class="oneinstructions">' + value.htmlEnc() + '</div>');
653 if(type == 'hidden') {
654 hideThis = ' style="display: none;"';
655 input = '<input name="' + encodeQuotes(field) + '" data-type="' + encodeQuotes(type) + '" type="hidden" class="dataform-i" value="' + encodeQuotes(value) + '" ' + required + ' />';
659 else if(type == 'boolean') {
667 input = '<input name="' + encodeQuotes(field) + '" type="checkbox" data-type="' + encodeQuotes(type) + '" class="dataform-i df-checkbox" ' + checked + required + ' />';
670 // List-single/list-multi field
671 else if((type == 'list-single') || (type == 'list-multi')) {
675 if(type == 'list-multi')
676 multiple = ' multiple=""';
678 // Append the select field
679 input = '<select name="' + encodeQuotes(field) + '" data-type="' + encodeQuotes(type) + '" class="dataform-i"' + required + multiple + '>';
682 // Append the available options
683 $(this).find('option').each(function() {
684 var nLabel = $(this).attr('label');
685 var nValue = $(this).find('value').text();
691 // If this is the selected value
693 selected = 'selected';
697 input += '<option ' + selected + ' value="' + encodeQuotes(nValue) + '">' + nLabel.htmlEnc() + '</option>';
700 input += '</select>';
704 else if(type == 'text-multi')
705 input = '<textarea rows="8" cols="60" data-type="' + encodeQuotes(type) + '" name="' + encodeQuotes(field) + '" class="dataform-i"' + required + '>' + value.htmlEnc() + '</textarea>';
708 else if(type == 'jid-multi') {
709 // Put the XID into an array
712 $(this).find('value').each(function() {
713 var cValue = $(this).text();
715 if(!existArrayValue(xid_arr, cValue))
716 xid_arr.push(cValue);
731 // Add the current XID
732 xid_value += xid_arr[i];
736 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 + ' />';
739 // Other stuffs that are similar
744 // Text-private field
745 if(type == 'text-private')
749 else if(type == 'jid-single')
752 input = '<input name="' + encodeQuotes(field) + '" data-type="' + encodeQuotes(type) + '" type="' + iType + '" class="dataform-i" value="' + encodeQuotes(value) + '"' + required + ' />';
755 // Append the HTML markup for this field
757 '<div class="oneresult ' + target + '-oneresult"' + hideThis + '>' +
758 '<label>' + label.htmlEnc() + '</label>' +
768 // Gets the dataform type
769 function getDataFormType(host, node, id) {
770 var iq = new JSJaCIQ();
771 iq.setID(id + '-' + genID());
775 var iqQuery = iq.setQuery(NS_DISCO_INFO);
778 iqQuery.setAttribute('node', node);
780 con.send(iq, handleThisBrowse);
783 // Handles the browse stanza
784 function handleThisBrowse(iq) {
785 /* REF: http://xmpp.org/registrar/disco-categories.html */
788 var splitted = id.split('-');
789 var target = splitted[0];
790 var sessionID = target + '-' + splitted[1];
791 var from = fullXID(getStanzaFrom(iq));
792 var hash = hex_md5(from);
793 var handleXML = iq.getQuery();
794 var pathID = '#' + target + ' .results[data-session=' + sessionID + ']';
796 // We first remove the waiting element
797 $(pathID + ' .disco-wait .' + hash).remove();
799 if($(handleXML).find('identity').attr('type')) {
800 var category = $(handleXML).find('identity').attr('category');
801 var type = $(handleXML).find('identity').attr('type');
802 var named = $(handleXML).find('identity').attr('name');
809 var one, two, three, four, five;
811 // Get the features that this entity supports
812 var findFeature = $(handleXML).find('feature');
814 for(i in findFeature) {
815 var current = findFeature.eq(i).attr('var');
843 var buttons = Array(one, two, three, four, five);
845 // We define the toolbox links depending on the supported features
847 var aTools = Array('search', 'join', 'subscribe', 'command', 'browse');
848 var bTools = Array(_e("Search"), _e("Join"), _e("Subscribe"), _e("Command"), _e("Browse"));
852 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>';
855 // As defined in the ref, we detect the type of each category to put an icon
861 case 'collaboration':
878 // We display the item we found
879 $(pathID + ' .disco-' + category + ' .disco-category-title').after(
880 '<div class="oneresult ' + target + '-oneresult ' + hash + '">' +
881 '<div class="one-icon ' + category + ' talk-images"></div>' +
882 '<div class="one-host">' + from + '</div>' +
883 '<div class="one-type">' + gName + '</div>' +
884 '<div class="one-actions">' + tools + '</div>' +
888 // We display the category
889 $(pathID + ' .disco-' + category).show();
893 $(pathID + ' .disco-others .disco-category-title').after(
894 '<div class="oneresult ' + target + '-oneresult">' +
895 '<div class="one-icon down talk-images"></div>' +
896 '<div class="one-host">' + from + '</div>' +
897 '<div class="one-type">' + _e("Service offline or broken") + '</div>' +
901 // We display the category
902 $(pathID + ' .disco-others').show();
905 // We hide the waiting stuffs if there's no remaining loading items
906 if(!$(pathID + ' .disco-wait .' + target + '-oneresult').size())
907 $(pathID + ' .disco-wait, #' + target + ' .wait').hide();
910 // Cleans the current data-form popup
911 function cleanDataForm(target) {
912 if(target == 'discovery')
915 $('#' + target + ' div.results').empty();
918 // Displays the no result indicator
919 function noResultDataForm(path) {
920 $(path).prepend('<p class="no-results">' + _e("Sorry, but the entity didn't return any result!") + '</p>');