1
0
Fork 0
mirror of https://github.com/YunoHost-Apps/jappix_ynh.git synced 2024-09-03 19:26:19 +02:00
jappix_ynh/source/app/javascripts/caps.js
titoko@titoko.fr 979b376609 update 1.0.1
2014-03-12 14:52:47 +01:00

541 lines
No EOL
16 KiB
JavaScript

/*
Jappix - An open social platform
These are the CAPS JS script for Jappix
-------------------------------------------------
License: AGPL
Author: Valérian Saliou, Maranda
*/
// Bundle
var Caps = (function () {
/**
* Alias of this
* @private
*/
var self = {};
/**
* Reads a stored Caps
* @public
* @param {string} caps
* @return {object}
*/
self.read = function(caps) {
try {
return Common.XMLFromString(
DataStore.getPersistent('global', 'caps', caps)
);
} catch(e) {
Console.error('Caps.read', e);
}
};
/**
* Returns an array of the Jappix disco#infos
* @public
* @return {object}
*/
self.myDiscoInfos = function() {
try {
var disco_base = [
NS_MUC,
NS_MUC_USER,
NS_MUC_ADMIN,
NS_MUC_OWNER,
NS_MUC_CONFIG,
NS_DISCO_INFO,
NS_DISCO_ITEMS,
NS_PUBSUB_RI,
NS_BOSH,
NS_CAPS,
NS_MOOD,
NS_ACTIVITY,
NS_TUNE,
NS_GEOLOC,
NS_NICK,
NS_URN_MBLOG,
NS_URN_INBOX,
NS_MOOD + NS_NOTIFY,
NS_ACTIVITY + NS_NOTIFY,
NS_TUNE + NS_NOTIFY,
NS_GEOLOC + NS_NOTIFY,
NS_URN_MBLOG + NS_NOTIFY,
NS_URN_INBOX + NS_NOTIFY,
NS_URN_DELAY,
NS_ROSTER,
NS_ROSTERX,
NS_HTTP_AUTH,
NS_CHATSTATES,
NS_XHTML_IM,
NS_URN_MAM,
NS_IPV6,
NS_LAST,
NS_PRIVATE,
NS_REGISTER,
NS_SEARCH,
NS_COMMANDS,
NS_VERSION,
NS_XDATA,
NS_VCARD,
NS_IETF_VCARD4,
NS_URN_ADATA,
NS_URN_AMETA,
NS_URN_TIME,
NS_URN_PING,
NS_URN_RECEIPTS,
NS_PRIVACY,
NS_IQOOB,
NS_XOOB,
NS_URN_CARBONS
];
var disco_jingle = JSJaCJingle_disco();
var disco_all = disco_base.concat(disco_jingle);
return disco_all;
} catch(e) {
Console.error('Caps.myDiscoInfos', e);
}
};
/**
* Gets the disco#infos of an entity
* @public
* @param {string} to
* @param {string} caps
* @return {boolean}
*/
self.getDiscoInfos = function(to, caps) {
try {
// No CAPS
if(!caps) {
Console.warn('No CAPS: ' + to);
self.displayDiscoInfos(to, '');
return false;
}
// Get the stored disco infos
var xml = self.read(caps);
// Yet stored
if(xml) {
Console.info('CAPS from cache: ' + to);
self.displayDiscoInfos(to, xml);
return true;
}
Console.info('CAPS from the network: ' + to);
// Not stored: get the disco#infos
var iq = new JSJaCIQ();
iq.setTo(to);
iq.setType('get');
iq.setQuery(NS_DISCO_INFO);
con.send(iq, self.handleDiscoInfos);
return true;
} catch(e) {
Console.error('Caps.getDiscoInfos', e);
}
};
/**
* Handles the disco#infos of an entity
* @public
* @param {object} iq
* @return {undefined}
*/
self.handleDiscoInfos = function(iq) {
try {
if(!iq || (iq.getType() == 'error'))
return;
// IQ received, get some values
var from = Common.fullXID(Common.getStanzaFrom(iq));
var query = iq.getQuery();
// Generate the CAPS-processing values
var identities = [];
var features = [];
var data_forms = [];
// Identity values
$(query).find('identity').each(function() {
var pCategory = $(this).attr('category');
var pType = $(this).attr('type');
var pLang = $(this).attr('xml:lang');
var pName = $(this).attr('name');
if(!pCategory)
pCategory = '';
if(!pType)
pType = '';
if(!pLang)
pLang = '';
if(!pName)
pName = '';
identities.push(pCategory + '/' + pType + '/' + pLang + '/' + pName);
});
// Feature values
$(query).find('feature').each(function() {
var pVar = $(this).attr('var');
// Add the current value to the array
if(pVar)
features.push(pVar);
});
// Data-form values
$(query).find('x[xmlns="' + NS_XDATA + '"]').each(function() {
// Initialize some stuffs
var pString = '';
var sortVar = [];
// Add the form type field
$(this).find('field[var="FORM_TYPE"] value').each(function() {
var cText = $(this).text();
if(cText)
pString += cText + '<';
});
// Add the var attributes into an array
$(this).find('field:not([var="FORM_TYPE"])').each(function() {
var cVar = $(this).attr('var');
if(cVar)
sortVar.push(cVar);
});
// Sort the var attributes
sortVar = sortVar.sort();
// Loop this sorted var attributes
$.each(sortVar, function(i) {
// Initialize the value sorting
var sortVal = [];
// Append it to the string
pString += sortVar[i] + '<';
// Add each value to the array
$(this).find('field[var=' + sortVar[i] + '] value').each(function() {
sortVal.push($(this).text());
});
// Sort the values
sortVal = sortVal.sort();
// Append the values to the string
for(var j in sortVal) {
pString += sortVal[j] + '<';
}
});
// Any string?
if(pString) {
// Remove the undesired double '<' from the string
if(pString.match(/(.+)(<)+$/))
pString = pString.substring(0, pString.length - 1);
// Add the current string to the array
data_forms.push(pString);
}
});
// Process the CAPS
var caps = self.process(identities, features, data_forms);
// Get the XML string
var xml = Common.xmlToString(query);
// Store the disco infos
DataStore.setPersistent('global', 'caps', caps, xml);
// This is our server
if(from == Utils.getServer()) {
// Handle the features
Features.handle(xml);
Console.info('Got our server CAPS');
} else {
// Display the disco infos
self.displayDiscoInfos(from, xml);
Console.info('Got CAPS: ' + from);
}
} catch(e) {
Console.error('Caps.handleDiscoInfos', e);
}
};
/**
* Displays the disco#infos everywhere needed for an entity
* @public
* @param {string} from
* @param {string} xml
* @return {undefined}
*/
self.displayDiscoInfos = function(from, xml) {
try {
// Generate the chat path
var xid = Common.bareXID(from);
// This comes from a private groupchat chat?
if(Utils.isPrivate(xid))
xid = from;
hash = hex_md5(xid);
// Display the supported features
var features = {};
$(xml).find('feature').each(function() {
var current = $(this).attr('var');
if(current) {
features[current] = 1;
}
});
// Paths
var path = $('#' + hash);
var roster_path = $('#roster .buddy.' + hash);
var roster_jingle_path = roster_path.find('.buddy-infos .call-jingle');
var message_area = path.find('.message-area');
var style = path.find('.chat-tools-style');
var jingle_audio = path.find('.tools-jingle-audio');
var roster_jingle_audio = roster_jingle_path.find('a.audio');
var jingle_video = path.find('.tools-jingle-video');
var roster_jingle_video = roster_jingle_path.find('a.video');
var roster_jingle_separator = roster_jingle_path.find('span.separator');
var file = path.find('.chat-tools-file');
// Apply xHTML-IM
if(NS_XHTML_IM in features) {
style.show();
} else {
// Remove the tooltip elements
style.hide();
style.find('.bubble-style').remove();
// Reset the markers
message_area.removeAttr('style')
.removeAttr('data-font')
.removeAttr('data-fontsize')
.removeAttr('data-color')
.removeAttr('data-bold')
.removeAttr('data-italic')
.removeAttr('data-underline');
}
// Apply Jingle
var jingle_local_supported = JSJAC_JINGLE_AVAILABLE;
var jingle_audio_xid = self.getFeatureResource(xid, NS_JINGLE_APPS_RTP_AUDIO);
var jingle_video_xid = self.getFeatureResource(xid, NS_JINGLE_APPS_RTP_VIDEO);
if(jingle_audio_xid && jingle_local_supported) {
jingle_audio.show();
roster_jingle_audio.show();
} else {
jingle_audio.hide();
roster_jingle_audio.hide();
}
if(jingle_video_xid && jingle_local_supported) {
jingle_video.show();
roster_jingle_video.show();
} else {
jingle_video.hide();
roster_jingle_video.hide();
}
if(jingle_audio_xid && jingle_video_xid && jingle_local_supported) {
roster_jingle_separator.show();
} else {
roster_jingle_separator.hide();
}
if((jingle_audio_xid || jingle_video_xid) && jingle_local_supported) {
roster_jingle_path.show();
} else {
roster_jingle_path.hide();
}
// Apply Out of Band Data
var iq_oob_xid = self.getFeatureResource(xid, NS_IQOOB);
if(iq_oob_xid || NS_XOOB in features) {
file.show();
// Set a marker
file.attr(
'data-oob',
iq_oob_xid ? 'iq' : 'x'
);
} else {
// Remove the tooltip elements
file.hide();
file.find('.bubble-style').remove();
// Reset the marker
file.removeAttr('data-oob');
}
// Apply receipts
if(NS_URN_RECEIPTS in features) {
message_area.attr('data-receipts', 'true');
} else {
message_area.removeAttr('data-receipts');
}
} catch(e) {
Console.error('Caps.displayDiscoInfos', e);
}
};
/**
* Generates the CAPS hash
* @public
* @param {object} cIdentities
* @param {object} cFeatures
* @param {object} cDataForms
* @return {string}
*/
self.process = function(cIdentities, cFeatures, cDataForms) {
try {
// Initialize
var cString = '';
// Sort the arrays
cIdentities = cIdentities.sort();
cFeatures = cFeatures.sort();
cDataForms = cDataForms.sort();
// Process the sorted identity string
for(var a in cIdentities) {
cString += cIdentities[a] + '<';
}
// Process the sorted feature string
for(var b in cFeatures) {
cString += cFeatures[b] + '<';
}
// Process the sorted data-form string
for(var c in cDataForms) {
cString += cDataForms[c] + '<';
}
// Process the SHA-1 hash
var cHash = b64_sha1(cString);
return cHash;
} catch(e) {
Console.error('Caps.process', e);
}
};
/**
* Generates the Jappix CAPS hash
* @public
* @return {string}
*/
self.mine = function() {
try {
return self.process(
['client/web//Jappix'],
self.myDiscoInfos(),
[]
);
} catch(e) {
Console.error('Caps.mine', e);
}
};
/**
* Returns the user resource supporting given feature w/ highest priority
* @public
* @param {string} xid
* @param {string} feature_ns
* @return {string}
*/
self.getFeatureResource = function(xid, feature_ns) {
var selected_xid = null;
try {
if(!feature_ns) {
throw 'No feature namespace given!';
}
var max_priority = null;
var cur_xid_full, cur_presence_sel, cur_caps, cur_features, cur_priority;
for(var cur_resource in Presence.resources(xid)) {
cur_xid_full = xid + '/' + cur_resource;
cur_presence_sel = $(Presence.readStanza(cur_xid_full));
cur_priority = parseInt((cur_presence_sel.find('priority').text() || 0), 10);
cur_caps = cur_presence_sel.find('caps').text();
if(cur_caps) {
cur_features = self.read(cur_caps);
if(cur_features && $(cur_features).find('feature[var="' + feature_ns + '"]').size() &&
(cur_priority >= max_priority || max_priority === null)) {
max_priority = cur_priority;
selected_xid = cur_xid_full;
}
}
}
} catch(e) {
Console.error('Caps.getFeatureResource', e);
} finally {
return selected_xid;
}
};
/**
* Return class scope
*/
return self;
})();