|
@@ -46,6 +46,7 @@
|
|
|
var converse = this;
|
|
|
|
|
|
// Constants
|
|
|
+ // ---------
|
|
|
var UNENCRYPTED = 0;
|
|
|
var UNVERIFIED= 1;
|
|
|
var VERIFIED= 2;
|
|
@@ -54,7 +55,8 @@
|
|
|
ENTER: 13
|
|
|
};
|
|
|
|
|
|
- // Default values
|
|
|
+ // Default configuration values
|
|
|
+ // ----------------------------
|
|
|
this.allow_contact_requests = true;
|
|
|
this.allow_muc = true;
|
|
|
this.allow_otr = true;
|
|
@@ -72,11 +74,8 @@
|
|
|
this.xhr_custom_status = false;
|
|
|
this.xhr_user_search = false;
|
|
|
|
|
|
- this.callback = callback || function () {};
|
|
|
-
|
|
|
- // Allow only the whitelisted settings attributes to be overwritten,
|
|
|
- // nothing else.
|
|
|
- whitelist = [
|
|
|
+ // Allow only whitelisted configuration attributes to be overwritten
|
|
|
+ _.extend(this, _.pick(settings, [
|
|
|
'allow_contact_requests',
|
|
|
'allow_muc',
|
|
|
'allow_otr',
|
|
@@ -98,9 +97,10 @@
|
|
|
'testing',
|
|
|
'xhr_custom_status',
|
|
|
'xhr_user_search'
|
|
|
- ];
|
|
|
- _.extend(this, _.pick(settings, whitelist));
|
|
|
+ ]));
|
|
|
|
|
|
+ // Translation machinery
|
|
|
+ // ---------------------
|
|
|
var __ = $.proxy(function (str) {
|
|
|
// Translation factory
|
|
|
if (this.i18n === undefined) {
|
|
@@ -124,6 +124,9 @@
|
|
|
*/
|
|
|
return str;
|
|
|
};
|
|
|
+
|
|
|
+ // Translation aware constants
|
|
|
+ // ---------------------------
|
|
|
var OTR_CLASS_MAPPING = {};
|
|
|
OTR_CLASS_MAPPING[UNENCRYPTED] = 'unencrypted';
|
|
|
OTR_CLASS_MAPPING[UNVERIFIED] = 'unverified';
|
|
@@ -136,7 +139,22 @@
|
|
|
OTR_TRANSLATED_MAPPING[VERIFIED] = __('verified');
|
|
|
OTR_TRANSLATED_MAPPING[FINISHED] = __('finished');
|
|
|
|
|
|
+ var STATUSES = {
|
|
|
+ 'dnd': __('This contact is busy'),
|
|
|
+ 'online': __('This contact is online'),
|
|
|
+ 'offline': __('This contact is offline'),
|
|
|
+ 'unavailable': __('This contact is unavailable'),
|
|
|
+ 'xa': __('This contact is away for an extended period'),
|
|
|
+ 'away': __('This contact is away')
|
|
|
+ };
|
|
|
+
|
|
|
+ // Module-level variables
|
|
|
+ // ----------------------
|
|
|
+ this.callback = callback || function () {};
|
|
|
this.msg_counter = 0;
|
|
|
+
|
|
|
+ // Module-level functions
|
|
|
+ // ----------------------
|
|
|
this.autoLink = function (text) {
|
|
|
// Convert URLs into hyperlinks
|
|
|
var re = /((http|https|ftp):\/\/[\w?=&.\/\-;#~%\-]+(?![\w\s?&.\/;#~%"=\-]*>))/g;
|
|
@@ -161,6 +179,46 @@
|
|
|
}
|
|
|
};
|
|
|
|
|
|
+ this.getVCard = function (jid, callback, errback) {
|
|
|
+ converse.connection.vcard.get(
|
|
|
+ $.proxy(function (iq) {
|
|
|
+ // Successful callback
|
|
|
+ $vcard = $(iq).find('vCard');
|
|
|
+ var fullname = $vcard.find('FN').text(),
|
|
|
+ img = $vcard.find('BINVAL').text(),
|
|
|
+ img_type = $vcard.find('TYPE').text(),
|
|
|
+ url = $vcard.find('URL').text();
|
|
|
+ if (jid) {
|
|
|
+ var rosteritem = converse.roster.get(jid);
|
|
|
+ if (rosteritem) {
|
|
|
+ rosteritem.save({
|
|
|
+ 'fullname': fullname || jid,
|
|
|
+ 'image_type': img_type,
|
|
|
+ 'image': img,
|
|
|
+ 'url': url,
|
|
|
+ 'vcard_updated': converse.toISOString(new Date())
|
|
|
+ });
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (callback) {
|
|
|
+ callback(jid, fullname, img, img_type, url);
|
|
|
+ }
|
|
|
+ }, this),
|
|
|
+ jid,
|
|
|
+ function (iq) {
|
|
|
+ // Error callback
|
|
|
+ var rosteritem = converse.roster.get(jid);
|
|
|
+ if (rosteritem) {
|
|
|
+ rosteritem.save({
|
|
|
+ 'vcard_updated': converse.toISOString(new Date())
|
|
|
+ });
|
|
|
+ }
|
|
|
+ if (errback) {
|
|
|
+ errback(iq);
|
|
|
+ }
|
|
|
+ });
|
|
|
+ };
|
|
|
+
|
|
|
this.onConnect = function (status) {
|
|
|
if (status === Strophe.Status.CONNECTED) {
|
|
|
converse.log('Connected');
|
|
@@ -257,51 +315,101 @@
|
|
|
this.updateMsgCounter();
|
|
|
};
|
|
|
|
|
|
- this.collections = {
|
|
|
- /* FIXME: XEP-0136 specifies 'urn:xmpp:archive' but the mod_archive_odbc
|
|
|
- * add-on for ejabberd wants the URL below. This might break for other
|
|
|
- * Jabber servers.
|
|
|
- */
|
|
|
- 'URI': 'http://www.xmpp.org/extensions/xep-0136.html#ns'
|
|
|
+ this.showControlBox = function () {
|
|
|
+ var controlbox = this.chatboxes.get('controlbox');
|
|
|
+ if (!controlbox) {
|
|
|
+ this.chatboxes.add({
|
|
|
+ id: 'controlbox',
|
|
|
+ box_id: 'controlbox',
|
|
|
+ visible: true
|
|
|
+ });
|
|
|
+ if (this.connection) {
|
|
|
+ this.chatboxes.get('controlbox').save();
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ controlbox.trigger('show');
|
|
|
+ }
|
|
|
};
|
|
|
|
|
|
- this.collections.getLastCollection = function (jid, callback) {
|
|
|
- var bare_jid = Strophe.getBareJidFromJid(jid),
|
|
|
- iq = $iq({'type':'get'})
|
|
|
- .c('list', {'xmlns': this.URI,
|
|
|
- 'with': bare_jid
|
|
|
- })
|
|
|
- .c('set', {'xmlns': 'http://jabber.org/protocol/rsm'})
|
|
|
- .c('before').up()
|
|
|
- .c('max')
|
|
|
- .t('1');
|
|
|
-
|
|
|
- converse.connection.sendIQ(iq,
|
|
|
- callback,
|
|
|
- function () {
|
|
|
- converse.log('Error while retrieving collections');
|
|
|
- });
|
|
|
+ this.toggleControlBox = function () {
|
|
|
+ if ($("div#controlbox").is(':visible')) {
|
|
|
+ var controlbox = this.chatboxes.get('controlbox');
|
|
|
+ if (this.connection) {
|
|
|
+ controlbox.destroy();
|
|
|
+ } else {
|
|
|
+ controlbox.trigger('hide');
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ this.showControlBox();
|
|
|
+ }
|
|
|
};
|
|
|
|
|
|
- this.collections.getLastMessages = function (jid, callback) {
|
|
|
- var that = this;
|
|
|
- this.getLastCollection(jid, function (result) {
|
|
|
- // Retrieve the last page of a collection (max 30 elements).
|
|
|
- var $collection = $(result).find('chat'),
|
|
|
- jid = $collection.attr('with'),
|
|
|
- start = $collection.attr('start'),
|
|
|
- iq = $iq({'type':'get'})
|
|
|
- .c('retrieve', {'start': start,
|
|
|
- 'xmlns': that.URI,
|
|
|
- 'with': jid
|
|
|
- })
|
|
|
- .c('set', {'xmlns': 'http://jabber.org/protocol/rsm'})
|
|
|
- .c('max')
|
|
|
- .t('30');
|
|
|
- converse.connection.sendIQ(iq, callback);
|
|
|
- });
|
|
|
+ this.initStatus = function (callback) {
|
|
|
+ this.xmppstatus = new this.XMPPStatus();
|
|
|
+ var id = hex_sha1('converse.xmppstatus-'+this.bare_jid);
|
|
|
+ this.xmppstatus.id = id; // This appears to be necessary for backbone.localStorage
|
|
|
+ this.xmppstatus.localStorage = new Backbone.LocalStorage(id);
|
|
|
+ this.xmppstatus.fetch({success: callback, error: callback});
|
|
|
};
|
|
|
|
|
|
+ this.initRoster = function () {
|
|
|
+ // Set up the roster
|
|
|
+ this.roster = new this.RosterItems();
|
|
|
+ this.roster.localStorage = new Backbone.LocalStorage(
|
|
|
+ hex_sha1('converse.rosteritems-'+converse.bare_jid));
|
|
|
+
|
|
|
+ // Register callbacks that depend on the roster
|
|
|
+ this.connection.roster.registerCallback(
|
|
|
+ $.proxy(this.roster.rosterHandler, this.roster),
|
|
|
+ null, 'presence', null);
|
|
|
+
|
|
|
+ this.connection.addHandler(
|
|
|
+ $.proxy(this.roster.subscribeToSuggestedItems, this.roster),
|
|
|
+ 'http://jabber.org/protocol/rosterx', 'message', null);
|
|
|
+
|
|
|
+ this.connection.addHandler(
|
|
|
+ $.proxy(function (presence) {
|
|
|
+ this.presenceHandler(presence);
|
|
|
+ return true;
|
|
|
+ }, this.roster), null, 'presence', null);
|
|
|
+
|
|
|
+ // No create the view which will fetch roster items from
|
|
|
+ // localStorage
|
|
|
+ this.rosterview = new this.RosterView({'model':this.roster});
|
|
|
+ };
|
|
|
+
|
|
|
+ this.onConnected = function () {
|
|
|
+ if (this.debug) {
|
|
|
+ this.connection.xmlInput = function (body) { console.log(body); };
|
|
|
+ this.connection.xmlOutput = function (body) { console.log(body); };
|
|
|
+ Strophe.log = function (level, msg) { console.log(level+' '+msg); };
|
|
|
+ Strophe.error = function (msg) { console.log('ERROR: '+msg); };
|
|
|
+ }
|
|
|
+ this.bare_jid = Strophe.getBareJidFromJid(this.connection.jid);
|
|
|
+ this.domain = Strophe.getDomainFromJid(this.connection.jid);
|
|
|
+ this.features = new this.Features();
|
|
|
+ this.initStatus($.proxy(function () {
|
|
|
+ this.initRoster();
|
|
|
+ this.chatboxes.onConnected();
|
|
|
+ this.connection.roster.get(function () {});
|
|
|
+
|
|
|
+ $(window).on("blur focus", $.proxy(function(e) {
|
|
|
+ if ((this.windowState != e.type) && (e.type == 'focus')) {
|
|
|
+ converse.clearMsgCounter();
|
|
|
+ }
|
|
|
+ this.windowState = e.type;
|
|
|
+ },this));
|
|
|
+ this.giveFeedback(__('Online Contacts'));
|
|
|
+ if (this.testing) {
|
|
|
+ this.callback(this);
|
|
|
+ } else {
|
|
|
+ this.callback();
|
|
|
+ }
|
|
|
+ }, this));
|
|
|
+ };
|
|
|
+
|
|
|
+ // Backbone Models and Views
|
|
|
+ // -------------------------
|
|
|
this.Message = Backbone.Model.extend();
|
|
|
|
|
|
this.Messages = Backbone.Collection.extend({
|
|
@@ -2275,19 +2383,11 @@
|
|
|
ask = item.get('ask'),
|
|
|
subscription = item.get('subscription');
|
|
|
|
|
|
- var statuses = {
|
|
|
- 'dnd': __('This contact is busy'),
|
|
|
- 'online': __('This contact is online'),
|
|
|
- 'offline': __('This contact is offline'),
|
|
|
- 'unavailable': __('This contact is unavailable'),
|
|
|
- 'xa': __('This contact is away for an extended period'),
|
|
|
- 'away': __('This contact is away')
|
|
|
- };
|
|
|
var classes_to_remove = [
|
|
|
'current-xmpp-contact',
|
|
|
'pending-xmpp-contact',
|
|
|
'requesting-xmpp-contact'
|
|
|
- ].concat(_.keys(statuses));
|
|
|
+ ].concat(_.keys(STATUSES));
|
|
|
|
|
|
_.each(classes_to_remove,
|
|
|
function (cls) {
|
|
@@ -2298,63 +2398,23 @@
|
|
|
|
|
|
this.$el.addClass(item.get('chat_status'));
|
|
|
|
|
|
- if ((ask === 'subscribe') && (converse.allow_contact_requests)) {
|
|
|
+ if (ask === 'subscribe') {
|
|
|
this.$el.addClass('pending-xmpp-contact');
|
|
|
this.$el.html(this.pending_template(item.toJSON()));
|
|
|
- } else if ((ask === 'request') && (converse.allow_contact_requests)) {
|
|
|
+ } else if (ask === 'request') {
|
|
|
this.$el.addClass('requesting-xmpp-contact');
|
|
|
this.$el.html(this.request_template(item.toJSON()));
|
|
|
converse.showControlBox();
|
|
|
- } else if (subscription === 'both' || ((subscription === 'to') && converse.allow_contact_requests)) {
|
|
|
+ } else if (subscription === 'both' || subscription === 'to') {
|
|
|
this.$el.addClass('current-xmpp-contact');
|
|
|
this.$el.html(this.template(
|
|
|
- _.extend(item.toJSON(), {'status_desc': statuses[item.get('chat_status')||'offline']})
|
|
|
- ));
|
|
|
+ _.extend(item.toJSON(), {'status_desc': STATUSES[item.get('chat_status')||'offline']})
|
|
|
+ ));
|
|
|
}
|
|
|
return this;
|
|
|
}
|
|
|
});
|
|
|
|
|
|
- this.getVCard = function (jid, callback, errback) {
|
|
|
- converse.connection.vcard.get(
|
|
|
- $.proxy(function (iq) {
|
|
|
- // Successful callback
|
|
|
- $vcard = $(iq).find('vCard');
|
|
|
- var fullname = $vcard.find('FN').text(),
|
|
|
- img = $vcard.find('BINVAL').text(),
|
|
|
- img_type = $vcard.find('TYPE').text(),
|
|
|
- url = $vcard.find('URL').text();
|
|
|
- if (jid) {
|
|
|
- var rosteritem = converse.roster.get(jid);
|
|
|
- if (rosteritem) {
|
|
|
- rosteritem.save({
|
|
|
- 'fullname': fullname || jid,
|
|
|
- 'image_type': img_type,
|
|
|
- 'image': img,
|
|
|
- 'url': url,
|
|
|
- 'vcard_updated': converse.toISOString(new Date())
|
|
|
- });
|
|
|
- }
|
|
|
- }
|
|
|
- if (callback) {
|
|
|
- callback(jid, fullname, img, img_type, url);
|
|
|
- }
|
|
|
- }, this),
|
|
|
- jid,
|
|
|
- function (iq) {
|
|
|
- // Error callback
|
|
|
- var rosteritem = converse.roster.get(jid);
|
|
|
- if (rosteritem) {
|
|
|
- rosteritem.save({
|
|
|
- 'vcard_updated': converse.toISOString(new Date())
|
|
|
- });
|
|
|
- }
|
|
|
- if (errback) {
|
|
|
- errback(iq);
|
|
|
- }
|
|
|
- });
|
|
|
- };
|
|
|
-
|
|
|
this.RosterItems = Backbone.Collection.extend({
|
|
|
model: converse.RosterItem,
|
|
|
comparator : function (rosteritem) {
|
|
@@ -2710,6 +2770,14 @@
|
|
|
chatbox.save(changes);
|
|
|
},
|
|
|
|
|
|
+ renderRosterItem: function () {
|
|
|
+ if ($.contains(document.documentElement, view.el)) {
|
|
|
+ view.render();
|
|
|
+ } else {
|
|
|
+ $my_contacts.after(view.render().el);
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
render: function (item) {
|
|
|
var $my_contacts = this.$el.find('#xmpp-contacts'),
|
|
|
$contact_requests = this.$el.find('#xmpp-contact-requests'),
|
|
@@ -2730,11 +2798,7 @@
|
|
|
$contact_requests.after(view.render().el);
|
|
|
$contact_requests.after($contact_requests.siblings('dd.requesting-xmpp-contact').tsort(crit));
|
|
|
} else if (subscription === 'both' || subscription === 'to') {
|
|
|
- if ($.contains(document.documentElement, view.el)) {
|
|
|
- view.render();
|
|
|
- } else {
|
|
|
- $my_contacts.after(view.render().el);
|
|
|
- }
|
|
|
+ this.renderRosterItem();
|
|
|
}
|
|
|
changed_presence = view.model.changed.chat_status;
|
|
|
if (changed_presence) {
|
|
@@ -3057,7 +3121,7 @@
|
|
|
|
|
|
showConnectButton: function () {
|
|
|
var $form = this.$el.find('#converse-login');
|
|
|
- var $button = $form.find('input[type=submit]')
|
|
|
+ var $button = $form.find('input[type=submit]');
|
|
|
if ($button.length) {
|
|
|
$button.show().siblings('span').remove();
|
|
|
}
|
|
@@ -3112,98 +3176,8 @@
|
|
|
}
|
|
|
});
|
|
|
|
|
|
- this.showControlBox = function () {
|
|
|
- var controlbox = this.chatboxes.get('controlbox');
|
|
|
- if (!controlbox) {
|
|
|
- this.chatboxes.add({
|
|
|
- id: 'controlbox',
|
|
|
- box_id: 'controlbox',
|
|
|
- visible: true
|
|
|
- });
|
|
|
- if (this.connection) {
|
|
|
- this.chatboxes.get('controlbox').save();
|
|
|
- }
|
|
|
- } else {
|
|
|
- controlbox.trigger('show');
|
|
|
- }
|
|
|
- };
|
|
|
-
|
|
|
- this.toggleControlBox = function () {
|
|
|
- if ($("div#controlbox").is(':visible')) {
|
|
|
- var controlbox = this.chatboxes.get('controlbox');
|
|
|
- if (this.connection) {
|
|
|
- controlbox.destroy();
|
|
|
- } else {
|
|
|
- controlbox.trigger('hide');
|
|
|
- }
|
|
|
- } else {
|
|
|
- this.showControlBox();
|
|
|
- }
|
|
|
- };
|
|
|
-
|
|
|
- this.initStatus = function (callback) {
|
|
|
- this.xmppstatus = new this.XMPPStatus();
|
|
|
- var id = hex_sha1('converse.xmppstatus-'+this.bare_jid);
|
|
|
- this.xmppstatus.id = id; // This appears to be necessary for backbone.localStorage
|
|
|
- this.xmppstatus.localStorage = new Backbone.LocalStorage(id);
|
|
|
- this.xmppstatus.fetch({success: callback, error: callback});
|
|
|
- };
|
|
|
-
|
|
|
- this.initRoster = function () {
|
|
|
- // Set up the roster
|
|
|
- this.roster = new this.RosterItems();
|
|
|
- this.roster.localStorage = new Backbone.LocalStorage(
|
|
|
- hex_sha1('converse.rosteritems-'+converse.bare_jid));
|
|
|
-
|
|
|
- // Register callbacks that depend on the roster
|
|
|
- this.connection.roster.registerCallback(
|
|
|
- $.proxy(this.roster.rosterHandler, this.roster),
|
|
|
- null, 'presence', null);
|
|
|
-
|
|
|
- this.connection.addHandler(
|
|
|
- $.proxy(this.roster.subscribeToSuggestedItems, this.roster),
|
|
|
- 'http://jabber.org/protocol/rosterx', 'message', null);
|
|
|
-
|
|
|
- this.connection.addHandler(
|
|
|
- $.proxy(function (presence) {
|
|
|
- this.presenceHandler(presence);
|
|
|
- return true;
|
|
|
- }, this.roster), null, 'presence', null);
|
|
|
-
|
|
|
- // No create the view which will fetch roster items from
|
|
|
- // localStorage
|
|
|
- this.rosterview = new this.RosterView({'model':this.roster});
|
|
|
- };
|
|
|
-
|
|
|
- this.onConnected = function () {
|
|
|
- if (this.debug) {
|
|
|
- this.connection.xmlInput = function (body) { console.log(body); };
|
|
|
- this.connection.xmlOutput = function (body) { console.log(body); };
|
|
|
- Strophe.log = function (level, msg) { console.log(level+' '+msg); };
|
|
|
- Strophe.error = function (msg) { console.log('ERROR: '+msg); };
|
|
|
- }
|
|
|
- this.bare_jid = Strophe.getBareJidFromJid(this.connection.jid);
|
|
|
- this.domain = Strophe.getDomainFromJid(this.connection.jid);
|
|
|
- this.features = new this.Features();
|
|
|
- this.initStatus($.proxy(function () {
|
|
|
- this.initRoster();
|
|
|
- this.chatboxes.onConnected();
|
|
|
- this.connection.roster.get(function () {});
|
|
|
-
|
|
|
- $(window).on("blur focus", $.proxy(function(e) {
|
|
|
- if ((this.windowState != e.type) && (e.type == 'focus')) {
|
|
|
- converse.clearMsgCounter();
|
|
|
- }
|
|
|
- this.windowState = e.type;
|
|
|
- },this));
|
|
|
- this.giveFeedback(__('Online Contacts'));
|
|
|
- if (this.testing) {
|
|
|
- this.callback(this);
|
|
|
- } else {
|
|
|
- this.callback();
|
|
|
- }
|
|
|
- }, this));
|
|
|
- };
|
|
|
+ // Initialization
|
|
|
+ // --------------
|
|
|
|
|
|
// This is the end of the initialize method.
|
|
|
this.chatboxes = new this.ChatBoxes();
|