3 Jappix - An open social platform
4 These are the vCard JS scripts for Jappix
6 -------------------------------------------------
10 Last revision: 16/01/12
14 // Opens the vCard popup
15 function openVCard() {
18 '<div class="top">' + _e("Your profile") + '</div>' +
21 '<a href="#" class="tab-active" data-key="1">' + _e("Identity") + '</a>' +
22 '<a href="#" data-key="2">' + _e("Profile image") + '</a>' +
23 '<a href="#" data-key="3">' + _e("Others") + '</a>' +
26 '<div class="content">' +
27 '<div id="lap1" class="lap-active one-lap forms">' +
29 '<legend>' + _e("Personal") + '</legend>' +
31 '<label for="USER-FN">' + _e("Complete name") + '</label>' +
32 '<input type="text" id="USER-FN" class="vcard-item" />' +
34 '<label for="USER-NICKNAME">' + _e("Nickname") + '</label>' +
35 '<input type="text" id="USER-NICKNAME" class="vcard-item" />' +
37 '<label for="USER-N-GIVEN">' + _e("First name") + '</label>' +
38 '<input type="text" id="USER-N-GIVEN" class="vcard-item" />' +
40 '<label for="USER-N-FAMILY">' + _e("Last name") + '</label>' +
41 '<input type="text" id="USER-N-FAMILY" class="vcard-item" />' +
43 '<label for="USER-BDAY">' + _e("Date of birth") + '</label>' +
44 '<input type="text" id="USER-BDAY" class="vcard-item" />' +
48 '<legend>' + _e("Contact") + '</legend>' +
50 '<label for="USER-EMAIL-USERID">' + _e("E-mail") + '</label>' +
51 '<input type="text" id="USER-EMAIL-USERID" class="vcard-item" />' +
53 '<label for="USER-TEL-NUMBER">' + _e("Phone") + '</label>' +
54 '<input type="text" id="USER-TEL-NUMBER" class="vcard-item" />' +
56 '<label for="USER-URL">' + _e("Website") + '</label>' +
57 '<input type="text" id="USER-URL" class="vcard-item" />' +
61 '<div id="lap2" class="one-lap forms">' +
63 '<legend>' + _e("New") + '</legend>' +
65 '<input type="hidden" id="USER-PHOTO-TYPE" class="vcard-item" />' +
66 '<input type="hidden" id="USER-PHOTO-BINVAL" class="vcard-item" />' +
68 '<form id="vcard-avatar" action="./php/avatar-upload.php" method="post" enctype="multipart/form-data">' +
74 '<legend>' + _e("Current") + '</legend>' +
76 '<div class="avatar-container"></div>' +
78 '<a href="#" class="one-button avatar-delete talk-images">' + _e("Delete") + '</a>' +
79 '<div class="no-avatar">' + _e("What a pity! You have no profile image defined in your identity card!") + '</div>' +
82 '<div class="avatar-wait avatar-info">' + _e("Please wait while your avatar is uploaded...") + '</div>' +
83 '<div class="avatar-ok avatar-info">' + _e("Here it is! A new beautiful profile image!") + '</div>' +
84 '<div class="avatar-error avatar-info">' + _e("The image file is not supported or has a bad size.") + '</div>' +
87 '<div id="lap3" class="one-lap forms">' +
89 '<legend>' + _e("Address") + '</legend>' +
91 '<label for="USER-ADR-STREET">' + _e("Street") + '</label>' +
92 '<input type="text" id="USER-ADR-STREET" class="vcard-item" />' +
94 '<label for="USER-ADR-LOCALITY">' + _e("City") + '</label>' +
95 '<input type="text" id="USER-ADR-LOCALITY" class="vcard-item" />' +
97 '<label for="USER-ADR-PCODE">' + _e("Postal code") + '</label>' +
98 '<input type="text" id="USER-ADR-PCODE" class="vcard-item" />' +
100 '<label for="USER-ADR-CTRY">' + _e("Country") + '</label>' +
101 '<input type="text" id="USER-ADR-CTRY" class="vcard-item" />' +
105 '<legend>' + _e("Biography") + '</legend>' +
107 '<textarea id="USER-DESC" rows="8" cols="60" class="vcard-item"></textarea>' +
111 '<div class="infos">' +
112 '<p class="infos-title">' + _e("Important notice") + '</p>' +
114 '<p>' + _e("Be careful of the information you write into your profile, because it could be accessed by everyone (even someone you don't want to).") + '</p>' +
115 '<p>' + _e("Not everything is private on XMPP; this is one of those things, your public profile (vCard).") + '</p>' +
116 '<p>' + printf(_e("It is strongly recommended to upload a profile image (%s maximum), like a picture of yourself, because that makes you easily recognizable by your friends."), JAPPIX_MAX_UPLOAD) + '</p>' +
117 '<p><a href="https://me.jappix.com/new" target="_blank">' + _e("Enable my public profile") + ' ยป</a></p>' +
121 '<div class="bottom">' +
122 '<div class="wait wait-medium"></div>' +
124 '<a href="#" class="finish save disabled">' + _e("Save") + '</a>' +
125 '<a href="#" class="finish cancel">' + _e("Cancel") + '</a>' +
129 createPopup('vcard', html);
131 // Associate the events
134 // We get the VCard informations
135 getVCard(getXID(), 'user');
140 // Closes the vCard popup
141 function closeVCard() {
143 destroyPopup('vcard');
145 // Create the welcome end popup?
152 // Switches the vCard popup tabs
153 function switchVCard(id) {
154 $('#vcard .one-lap').removeClass('lap-active');
155 $('#vcard #lap' + id).addClass('lap-active');
156 $('#vcard .tab a').removeClass('tab-active');
157 $('#vcard .tab a[data-key=' + id + ']').addClass('tab-active');
162 // Waits for the avatar upload reply
163 function waitAvatarUpload() {
164 // Reset the avatar info
165 $('#vcard .avatar-info').hide().stopTime();
167 // Show the wait info
168 $('#vcard .avatar-wait').show();
171 // Handles the avatar upload reply
172 function handleAvatarUpload(responseXML) {
174 var dData = $(responseXML).find('jappix');
176 // Not current upload session?
177 if(parseInt(dData.attr('id')) != parseInt($('#vcard-avatar input[name=id]').val()))
180 // Reset the avatar info
181 $('#vcard .avatar-info').hide().stopTime();
183 // Process the returned data
184 if(!dData.find('error').size()) {
186 var aType = dData.find('type').text();
187 var aBinval = dData.find('binval').text();
189 // We remove everything that isn't useful right here
190 $('#vcard .no-avatar').hide();
191 $('#vcard .avatar').remove();
193 // We display the delete button
194 $('#vcard .avatar-delete').show();
196 // We tell the user it's okay
197 $('#vcard .avatar-ok').show();
200 $('#vcard .avatar-info').oneTime('10s', function() {
204 // We put the base64 values in a hidden input to be sent
205 $('#USER-PHOTO-TYPE').val(aType);
206 $('#USER-PHOTO-BINVAL').val(aBinval);
208 // We display the avatar !
209 $('#vcard .avatar-container').replaceWith('<div class="avatar-container"><img class="avatar" src="data:' + aType + ';base64,' + aBinval + '" alt="" /></div>');
214 $('#vcard .avatar-error').show();
217 $('#vcard .avatar-info').oneTime('10s', function() {
221 logThis('Error while uploading the avatar: ' + dData.find('error').text(), 1);
225 // Deletes the encoded avatar of an user
226 function deleteAvatar() {
227 // We remove the avatar displayed elements
228 $('#vcard .avatar-info').stopTime();
229 $('#vcard .avatar-info, #vcard .avatar-wait, #vcard .avatar-error, #vcard .avatar-ok, #vcard .avatar-delete').hide();
230 $('#vcard .avatar').remove();
232 // We reset the input value
233 $('#USER-PHOTO-TYPE, #USER-PHOTO-BINVAL').val('');
235 // We show the avatar-uploading request
236 $('#vcard .no-avatar').show();
241 // Creates a special vCard input
242 function createInputVCard(id, type) {
243 // Generate the new ID
246 // Can append the content
247 if((type == 'user') && !exists('#vcard #' + id))
248 $('#vcard .content').append('<input id="' + id + '" class="vcard-item" type="hidden" />');
251 // Gets the vCard of a XID
252 function getVCard(to, type) {
253 // Generate a special ID
257 var iq = new JSJaCIQ();
260 iq.appendNode('vCard', {'xmlns': NS_VCARD});
262 // Send the IQ to the good user
264 // Show the wait icon
265 $('#vcard .wait').show();
267 // Apply the session ID
268 $('#vcard').attr('data-vcard', id);
271 con.send(iq, handeUVCard);
275 // Show the wait icon
276 $('#userinfos .wait').show();
278 // Apply the session ID
279 $('#userinfos').attr('data-vcard', id);
283 con.send(iq, handeBVCard);
287 // Handles the current connected user's vCard
288 function handeUVCard(iq) {
289 handleVCard(iq, 'user');
292 // Handles an external buddy vCard
293 function handeBVCard(iq) {
294 handleVCard(iq, 'buddy');
297 // Handles a vCard stanza
298 function handleVCard(iq, type) {
300 var iqID = iq.getID();
301 var iqFrom = fullXID(getStanzaFrom(iq));
302 var iqNode = iq.getNode();
305 var path_vCard = '#vcard[data-vcard=' + iqID + ']';
306 var path_userInfos = '#userinfos[data-vcard=' + iqID + ']';
308 // End if the session does not exist
309 if(((type == 'user') && !exists(path_vCard)) || ((type == 'buddy') && !exists(path_userInfos)))
312 // We retrieve main values
315 $(iqNode).find('vCard').children().each(function() {
316 // Read the current parent node name
317 var tokenname = (this).nodeName.toUpperCase();
319 // Node with a parent
320 if($(this).children().size()) {
321 $(this).children().each(function() {
322 // Get the node values
323 var currentID = tokenname + '-' + (this).nodeName.toUpperCase();
324 var currentText = $(this).text();
327 if(!existArrayValue(values_yet, currentID)) {
328 // Create an input if it does not exist
329 createInputVCard(values_yet, type);
331 // Userinfos viewer popup
332 if((type == 'buddy') && currentText) {
333 if(currentID == 'EMAIL-USERID')
334 $(path_userInfos + ' #BUDDY-' + currentID).html('<a href="mailto:' + currentText.htmlEnc() + '" target="_blank">' + currentText.htmlEnc() + '</a>');
336 $(path_userInfos + ' #BUDDY-' + currentID).text(currentText.htmlEnc());
339 // Profile editor popup
340 else if(type == 'user')
341 $(path_vCard + ' #USER-' + currentID).val(currentText);
343 // Avoid duplicating the value
344 values_yet.push(currentID);
349 // Node without any parent
351 // Get the node values
352 var currentText = $(this).text();
355 if(!existArrayValue(values_yet, tokenname)) {
356 // Create an input if it does not exist
357 createInputVCard(tokenname, type);
359 // Userinfos viewer popup
360 if((type == 'buddy') && currentText) {
362 if(tokenname == 'URL') {
363 // No http:// or https:// prefix, we should add it
364 if(!currentText.match(/^https?:\/\/(.+)/))
365 currentText = 'http://' + currentText;
367 currentText = '<a href="' + currentText + '" target="_blank">' + currentText.htmlEnc() + '</a>';
370 // Description modification
371 else if(tokenname == 'DESC')
372 currentText = filterThisMessage(currentText, getBuddyName(iqFrom).htmlEnc(), true);
376 currentText = currentText.htmlEnc();
378 $(path_userInfos + ' #BUDDY-' + tokenname).html(currentText);
381 // Profile editor popup
382 else if(type == 'user')
383 $(path_vCard + ' #USER-' + tokenname).val(currentText);
385 // Avoid duplicating the value
386 values_yet.push(tokenname);
391 // Update the stored avatar
392 if(type == 'buddy') {
393 // Get the avatar XML
394 var xml = getPersistent('avatar', iqFrom);
396 // If there were no stored avatar previously
397 if($(XMLFromString(xml)).find('type').text() == 'none') {
398 xml = xml.replace(/<forced>false<\/forced>/gi, '<forced>true</forced>');
399 setPersistent('avatar', iqFrom, xml);
402 // Handle the user avatar
406 // The avatar values targets
407 var aBinval, aType, aContainer;
410 aBinval = $('#USER-PHOTO-BINVAL').val();
411 aType = $('#USER-PHOTO-TYPE').val();
412 aContainer = path_vCard + ' .avatar-container';
416 aBinval = $(iqNode).find('BINVAL:first').text();
417 aType = $(iqNode).find('TYPE:first').text();
418 aContainer = path_userInfos + ' .avatar-container';
421 // We display the avatar if retrieved
428 // We move all the things that we don't need in that case
429 $(path_vCard + ' .no-avatar').hide();
430 $(path_vCard + ' .avatar-delete').show();
431 $(path_vCard + ' .avatar').remove();
434 // We display the avatar we have just received
435 $(aContainer).replaceWith('<div class="avatar-container"><img class="avatar" src="data:' + aType + ';base64,' + aBinval + '" alt="" /></div>');
438 else if(type == 'buddy')
439 $(aContainer).replaceWith('<div class="avatar-container"><img class="avatar" src="' + './img/others/default-avatar.png' + '" alt="" /></div>');
441 // Do someting depending of the type
443 $(path_vCard + ' .wait').hide();
444 $(path_vCard + ' .finish:first').removeClass('disabled');
450 logThis('vCard received: ' + iqFrom);
453 // Sends the vCard of the user
454 function sendVCard() {
456 var iq = new JSJaCIQ();
459 var vCard = iq.appendNode('vCard', {'xmlns': NS_VCARD});
461 // We send the identity part of the form
462 $('#vcard .vcard-item').each(function() {
463 var itemID = $(this).attr('id').replace(/^USER-(.+)/, '$1');
464 var itemVal = $(this).val();
466 if(itemVal && itemID) {
467 if(itemID.indexOf('-') != -1) {
468 var tagname = explodeThis('-', itemID, 0);
471 if(vCard.getElementsByTagName(tagname).length > 0)
472 aNode = vCard.getElementsByTagName(tagname).item(0);
474 aNode = vCard.appendChild(iq.buildNode(tagname, {'xmlns': NS_VCARD}));
476 aNode.appendChild(iq.buildNode(explodeThis('-', itemID, 1), {'xmlns': NS_VCARD}, itemVal));
480 vCard.appendChild(iq.buildNode(itemID, {'xmlns': NS_VCARD}, itemVal));
487 // Send the user nickname & avatar over PEP
490 var user_nick = $('#USER-NICKNAME').val();
491 var photo_bin = $('#USER-PHOTO-BINVAL').val();
492 var photo_type = $('#USER-PHOTO-TYPE').val();
493 var photo_data = Base64.decode(photo_bin) || '';
494 var photo_bytes = photo_data.length || '';
495 var photo_id = hex_sha1(photo_data) || '';
501 [photo_type, photo_id, photo_bytes]
513 var iq = new JSJaCIQ();
516 var pubsub = iq.appendNode('pubsub', {'xmlns': NS_PUBSUB});
517 var publish = pubsub.appendChild(iq.buildNode('publish', {'node': node[i], 'xmlns': NS_PUBSUB}));
519 if((i == 0) && read[0]) {
520 var item = publish.appendChild(iq.buildNode('item', {'xmlns': NS_PUBSUB}));
523 item.appendChild(iq.buildNode('nick', {'xmlns': NS_NICK}, read[i]));
526 else if(((i == 1) || (i == 2)) && read[1]) {
527 var item = publish.appendChild(iq.buildNode('item', {'xmlns': NS_PUBSUB}));
529 // Apply the SHA-1 hash
531 item.setAttribute('id', photo_id);
535 item.appendChild(iq.buildNode('data', {'xmlns': NS_URN_ADATA}, read[i]));
537 // Avatar metadata node
539 var metadata = item.appendChild(iq.buildNode('metadata', {'xmlns': NS_URN_AMETA}));
542 var meta_info = metadata.appendChild(iq.buildNode('info', {'xmlns': NS_URN_AMETA}));
545 meta_info.setAttribute('type', read[i][0]);
547 meta_info.setAttribute('id', read[i][1]);
549 meta_info.setAttribute('bytes', read[i][2]);
558 // Close the vCard stuffs
561 // Get our new avatar
562 getAvatar(getXID(), 'force', 'true', 'forget');
564 logThis('vCard sent.');
570 function launchVCard() {
571 // Focus on the first input
572 $(document).oneTime(10, function() {
573 $('#vcard input:first').focus();
577 $('#vcard input[type=text]').keyup(function(e) {
578 // Enter pressed: send the vCard
579 if((e.keyCode == 13) && !$('#vcard .finish.save').hasClass('disabled'))
584 $('#vcard .tab a').click(function() {
586 if($(this).hasClass('tab-active'))
589 // Switch to the good tab
590 var key = parseInt($(this).attr('data-key'));
592 return switchVCard(key);
595 $('#vcard .avatar-delete').click(function() {
596 return deleteAvatar();
599 $('#vcard .bottom .finish').click(function() {
600 if($(this).is('.cancel'))
602 if($(this).is('.save') && !$(this).hasClass('disabled'))
608 // Avatar upload vars
609 var avatar_options = {
611 beforeSubmit: waitAvatarUpload,
612 success: handleAvatarUpload
615 // Avatar upload form submit event
616 $('#vcard-avatar').submit(function() {
617 if($('#vcard .wait').is(':hidden') && $('#vcard .avatar-info.avatar-wait').is(':hidden') && $('#vcard-avatar input[type=file]').val())
618 $(this).ajaxSubmit(avatar_options);
623 // Avatar upload input change event
624 $('#vcard-avatar input[type=file]').change(function() {
625 if($('#vcard .wait').is(':hidden') && $('#vcard .avatar-info.avatar-wait').is(':hidden') && $(this).val())
626 $('#vcard-avatar').ajaxSubmit(avatar_options);