Merge remote-tracking branch 'origin/master'
This commit is contained in:
commit
6b09416ba6
@ -1755,31 +1755,42 @@
|
|||||||
test_utils.openContactsPanel(_converse);
|
test_utils.openContactsPanel(_converse);
|
||||||
test_utils.waitUntil(function () {
|
test_utils.waitUntil(function () {
|
||||||
return _converse.rosterview.$el.find('dt').length;
|
return _converse.rosterview.$el.find('dt').length;
|
||||||
}, 300).then(function () {
|
}, 500).then(function () {
|
||||||
// Make the timeouts shorter so that we can test
|
// Make the timeouts shorter so that we can test
|
||||||
_converse.TIMEOUTS.PAUSED = 200;
|
_converse.TIMEOUTS.PAUSED = 200;
|
||||||
_converse.TIMEOUTS.INACTIVE = 200;
|
_converse.TIMEOUTS.INACTIVE = 200;
|
||||||
contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
|
contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
|
||||||
test_utils.openChatBoxFor(_converse, contact_jid);
|
test_utils.openChatBoxFor(_converse, contact_jid);
|
||||||
view = _converse.chatboxviews.get(contact_jid);
|
view = _converse.chatboxviews.get(contact_jid);
|
||||||
|
return test_utils.waitUntil(function () {
|
||||||
|
return view.model.get('chat_state') === 'active';
|
||||||
|
}, 500);
|
||||||
|
}).then(function () {
|
||||||
|
console.log('chat_state set to active');
|
||||||
|
view = _converse.chatboxviews.get(contact_jid);
|
||||||
expect(view.model.get('chat_state')).toBe('active');
|
expect(view.model.get('chat_state')).toBe('active');
|
||||||
view.keyPressed({
|
view.keyPressed({
|
||||||
target: view.$el.find('textarea.chat-textarea'),
|
target: view.$el.find('textarea.chat-textarea'),
|
||||||
keyCode: 1
|
keyCode: 1
|
||||||
});
|
});
|
||||||
|
return test_utils.waitUntil(function () {
|
||||||
|
return view.model.get('chat_state') === 'composing';
|
||||||
|
}, 500);
|
||||||
|
}).then(function () {
|
||||||
|
console.log('chat_state set to composing');
|
||||||
|
view = _converse.chatboxviews.get(contact_jid);
|
||||||
expect(view.model.get('chat_state')).toBe('composing');
|
expect(view.model.get('chat_state')).toBe('composing');
|
||||||
spyOn(_converse.connection, 'send');
|
spyOn(_converse.connection, 'send');
|
||||||
return test_utils.waitUntil(function () {
|
return test_utils.waitUntil(function () {
|
||||||
if (view.model.get('chat_state') === 'paused') {
|
return view.model.get('chat_state') === 'paused';
|
||||||
return true;
|
}, 500);
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}, 300);
|
|
||||||
}).then(function () {
|
}).then(function () {
|
||||||
|
console.log('chat_state set to paused');
|
||||||
return test_utils.waitUntil(function () {
|
return test_utils.waitUntil(function () {
|
||||||
return view.model.get('chat_state') === 'inactive';
|
return view.model.get('chat_state') === 'inactive';
|
||||||
}, 300);
|
}, 500);
|
||||||
}).then(function () {
|
}).then(function () {
|
||||||
|
console.log('chat_state set to inactive');
|
||||||
expect(_converse.connection.send).toHaveBeenCalled();
|
expect(_converse.connection.send).toHaveBeenCalled();
|
||||||
var calls = _.filter(_converse.connection.send.calls.all(), function (call) {
|
var calls = _.filter(_converse.connection.send.calls.all(), function (call) {
|
||||||
return call.args[0] instanceof Strophe.Builder;
|
return call.args[0] instanceof Strophe.Builder;
|
||||||
@ -1799,7 +1810,7 @@
|
|||||||
expect($stanza.children().get(1).tagName).toBe('no-store');
|
expect($stanza.children().get(1).tagName).toBe('no-store');
|
||||||
expect($stanza.children().get(2).tagName).toBe('no-permanent-store');
|
expect($stanza.children().get(2).tagName).toBe('no-permanent-store');
|
||||||
done();
|
done();
|
||||||
});
|
}).catch(_.partial(_converse.log, _, Strophe.LogLevel.FATAL));
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it("is sent when the user a minimizes a chat box",
|
it("is sent when the user a minimizes a chat box",
|
||||||
|
115
spec/register.js
115
spec/register.js
@ -3,20 +3,42 @@
|
|||||||
} (this, function ($, jasmine, mock, converse, test_utils) {
|
} (this, function ($, jasmine, mock, converse, test_utils) {
|
||||||
var Strophe = converse.env.Strophe;
|
var Strophe = converse.env.Strophe;
|
||||||
var $iq = converse.env.$iq;
|
var $iq = converse.env.$iq;
|
||||||
|
var _ = converse.env._;
|
||||||
|
|
||||||
describe("The Registration Panel", function () {
|
describe("The Registration Panel", function () {
|
||||||
|
|
||||||
it("is not available unless allow_registration=true", mock.initConverse(function (_converse) {
|
it("is not available unless allow_registration=true",
|
||||||
|
mock.initConverseWithPromises(
|
||||||
|
null, ['connectionInitialized', 'chatBoxesInitialized'],
|
||||||
|
{ auto_login: false,
|
||||||
|
allow_registration: false },
|
||||||
|
function (done, _converse) {
|
||||||
|
|
||||||
|
test_utils.waitUntil(function () {
|
||||||
|
return _converse.chatboxviews.get('controlbox');
|
||||||
|
}, 300)
|
||||||
|
.then(function () {
|
||||||
|
|
||||||
test_utils.openControlBox();
|
test_utils.openControlBox();
|
||||||
var cbview = _converse.chatboxviews.get('controlbox');
|
var cbview = _converse.chatboxviews.get('controlbox');
|
||||||
expect(cbview.$('#controlbox-tabs li').length).toBe(1);
|
expect(cbview.$('#controlbox-tabs li').length).toBe(1);
|
||||||
expect(cbview.$('#controlbox-tabs li').text().trim()).toBe("Sign in");
|
expect(cbview.$('#controlbox-tabs li').text().trim()).toBe("Sign in");
|
||||||
|
done();
|
||||||
}, { auto_login: false,
|
});
|
||||||
allow_registration: false,
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it("can be opened by clicking on the registration tab", mock.initConverse(function (_converse) {
|
it("can be opened by clicking on the registration tab",
|
||||||
|
mock.initConverseWithPromises(
|
||||||
|
null, ['connectionInitialized', 'chatBoxesInitialized'],
|
||||||
|
{ auto_login: false,
|
||||||
|
allow_registration: true },
|
||||||
|
function (done, _converse) {
|
||||||
|
|
||||||
|
test_utils.waitUntil(function () {
|
||||||
|
return _.get(_converse.chatboxviews.get('controlbox'), 'registerpanel');
|
||||||
|
}, 300)
|
||||||
|
.then(function () {
|
||||||
|
|
||||||
var cbview = _converse.chatboxviews.get('controlbox');
|
var cbview = _converse.chatboxviews.get('controlbox');
|
||||||
test_utils.openControlBox();
|
test_utils.openControlBox();
|
||||||
var $tabs = cbview.$('#controlbox-tabs');
|
var $tabs = cbview.$('#controlbox-tabs');
|
||||||
@ -32,12 +54,22 @@
|
|||||||
expect($login.is(':visible')).toBe(false);
|
expect($login.is(':visible')).toBe(false);
|
||||||
expect($registration.is(':visible')).toBe(true);
|
expect($registration.is(':visible')).toBe(true);
|
||||||
expect(cbview.switchTab).toHaveBeenCalled();
|
expect(cbview.switchTab).toHaveBeenCalled();
|
||||||
|
done();
|
||||||
}, { auto_login: false,
|
});
|
||||||
allow_registration: true,
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it("allows the user to choose an XMPP provider's domain", mock.initConverse(function (_converse) {
|
it("allows the user to choose an XMPP provider's domain",
|
||||||
|
mock.initConverseWithPromises(
|
||||||
|
null, ['connectionInitialized', 'chatBoxesInitialized'],
|
||||||
|
{ auto_login: false,
|
||||||
|
allow_registration: true },
|
||||||
|
function (done, _converse) {
|
||||||
|
|
||||||
|
test_utils.waitUntil(function () {
|
||||||
|
return _.get(_converse.chatboxviews.get('controlbox'), 'registerpanel');
|
||||||
|
}, 300)
|
||||||
|
.then(function () {
|
||||||
|
|
||||||
var cbview = _converse.chatboxviews.get('controlbox');
|
var cbview = _converse.chatboxviews.get('controlbox');
|
||||||
var registerview = cbview.registerpanel;
|
var registerview = cbview.registerpanel;
|
||||||
spyOn(registerview, 'onProviderChosen').and.callThrough();
|
spyOn(registerview, 'onProviderChosen').and.callThrough();
|
||||||
@ -59,11 +91,22 @@
|
|||||||
$form.find('input[type=submit]').click();
|
$form.find('input[type=submit]').click();
|
||||||
expect(registerview.onProviderChosen).toHaveBeenCalled();
|
expect(registerview.onProviderChosen).toHaveBeenCalled();
|
||||||
expect(_converse.connection.connect).toHaveBeenCalled();
|
expect(_converse.connection.connect).toHaveBeenCalled();
|
||||||
}, { auto_login: false,
|
done();
|
||||||
allow_registration: true,
|
});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
it("will render a registration form as received from the XMPP provider",
|
||||||
|
mock.initConverseWithPromises(
|
||||||
|
null, ['connectionInitialized', 'chatBoxesInitialized'],
|
||||||
|
{ auto_login: false,
|
||||||
|
allow_registration: true },
|
||||||
|
function (done, _converse) {
|
||||||
|
|
||||||
|
test_utils.waitUntil(function () {
|
||||||
|
return _.get(_converse.chatboxviews.get('controlbox'), 'registerpanel');
|
||||||
|
}, 300)
|
||||||
|
.then(function () {
|
||||||
|
|
||||||
it("will render a registration form as received from the XMPP provider", mock.initConverse(function (_converse) {
|
|
||||||
var cbview = _converse.chatboxviews.get('controlbox');
|
var cbview = _converse.chatboxviews.get('controlbox');
|
||||||
cbview.$('#controlbox-tabs').find('li').last().find('a').click(); // Click the Register tab
|
cbview.$('#controlbox-tabs').find('li').last().find('a').click(); // Click the Register tab
|
||||||
var registerview = _converse.chatboxviews.get('controlbox').registerpanel;
|
var registerview = _converse.chatboxviews.get('controlbox').registerpanel;
|
||||||
@ -107,14 +150,25 @@
|
|||||||
expect(registerview.$('input').length).toBe(5);
|
expect(registerview.$('input').length).toBe(5);
|
||||||
expect(registerview.$('input[type=submit]').length).toBe(1);
|
expect(registerview.$('input[type=submit]').length).toBe(1);
|
||||||
expect(registerview.$('input[type=button]').length).toBe(1);
|
expect(registerview.$('input[type=button]').length).toBe(1);
|
||||||
}, { auto_login: false,
|
done();
|
||||||
allow_registration: true,
|
});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
it("will set form_type to legacy and submit it as legacy",
|
||||||
|
mock.initConverseWithPromises(
|
||||||
|
null, ['connectionInitialized', 'chatBoxesInitialized'],
|
||||||
|
{ auto_login: false,
|
||||||
|
allow_registration: true },
|
||||||
|
function (done, _converse) {
|
||||||
|
|
||||||
|
test_utils.waitUntil(function () {
|
||||||
|
return _.get(_converse.chatboxviews.get('controlbox'), 'registerpanel');
|
||||||
|
}, 300)
|
||||||
|
.then(function () {
|
||||||
|
|
||||||
it("will set form_type to legacy and submit it as legacy", mock.initConverse(function (_converse) {
|
|
||||||
var cbview = _converse.chatboxviews.get('controlbox');
|
var cbview = _converse.chatboxviews.get('controlbox');
|
||||||
cbview.$('#controlbox-tabs').find('li').last().find('a').click(); // Click the Register tab
|
cbview.$('#controlbox-tabs').find('li').last().find('a').click(); // Click the Register tab
|
||||||
var registerview = _converse.chatboxviews.get('controlbox').registerpanel;
|
var registerview = cbview.registerpanel;
|
||||||
spyOn(registerview, 'onProviderChosen').and.callThrough();
|
spyOn(registerview, 'onProviderChosen').and.callThrough();
|
||||||
spyOn(registerview, 'getRegistrationFields').and.callThrough();
|
spyOn(registerview, 'getRegistrationFields').and.callThrough();
|
||||||
spyOn(registerview, 'onRegistrationFields').and.callThrough();
|
spyOn(registerview, 'onRegistrationFields').and.callThrough();
|
||||||
@ -156,11 +210,22 @@
|
|||||||
var $stanza = $(_converse.connection.send.calls.argsFor(0)[0].tree());
|
var $stanza = $(_converse.connection.send.calls.argsFor(0)[0].tree());
|
||||||
expect($stanza.children('query').children().length).toBe(3);
|
expect($stanza.children('query').children().length).toBe(3);
|
||||||
expect($stanza.children('query').children()[0].tagName).toBe('username');
|
expect($stanza.children('query').children()[0].tagName).toBe('username');
|
||||||
}, { auto_login: false,
|
done();
|
||||||
allow_registration: true,
|
});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
it("will set form_type to xform and submit it as xform",
|
||||||
|
mock.initConverseWithPromises(
|
||||||
|
null, ['connectionInitialized', 'chatBoxesInitialized'],
|
||||||
|
{ auto_login: false,
|
||||||
|
allow_registration: true },
|
||||||
|
function (done, _converse) {
|
||||||
|
|
||||||
|
test_utils.waitUntil(function () {
|
||||||
|
return _.get(_converse.chatboxviews.get('controlbox'), 'registerpanel');
|
||||||
|
}, 300)
|
||||||
|
.then(function () {
|
||||||
|
|
||||||
it("will set form_type to xform and submit it as xform", mock.initConverse(function (_converse) {
|
|
||||||
var cbview = _converse.chatboxviews.get('controlbox');
|
var cbview = _converse.chatboxviews.get('controlbox');
|
||||||
cbview.$('#controlbox-tabs').find('li').last().find('a').click(); // Click the Register tab
|
cbview.$('#controlbox-tabs').find('li').last().find('a').click(); // Click the Register tab
|
||||||
var registerview = _converse.chatboxviews.get('controlbox').registerpanel;
|
var registerview = _converse.chatboxviews.get('controlbox').registerpanel;
|
||||||
@ -208,8 +273,8 @@
|
|||||||
expect($stanza.children('query').children().length).toBe(1);
|
expect($stanza.children('query').children().length).toBe(1);
|
||||||
expect($stanza.children('query').children().children().length).toBe(3);
|
expect($stanza.children('query').children().children().length).toBe(3);
|
||||||
expect($stanza.children('query').children().children()[0].tagName).toBe('field');
|
expect($stanza.children('query').children().children()[0].tagName).toBe('field');
|
||||||
}, { auto_login: false,
|
done();
|
||||||
allow_registration: true,
|
});
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
|
@ -53,6 +53,7 @@ require.config({
|
|||||||
"inverse": "src/inverse",
|
"inverse": "src/inverse",
|
||||||
|
|
||||||
"converse-bookmarks": "src/converse-bookmarks",
|
"converse-bookmarks": "src/converse-bookmarks",
|
||||||
|
"converse-chatboxes": "src/converse-chatboxes",
|
||||||
"converse-chatview": "src/converse-chatview",
|
"converse-chatview": "src/converse-chatview",
|
||||||
"converse-controlbox": "src/converse-controlbox",
|
"converse-controlbox": "src/converse-controlbox",
|
||||||
"converse-core": "src/converse-core",
|
"converse-core": "src/converse-core",
|
||||||
|
384
src/converse-chatboxes.js
Normal file
384
src/converse-chatboxes.js
Normal file
@ -0,0 +1,384 @@
|
|||||||
|
// Converse.js (A browser based XMPP chat client)
|
||||||
|
// http://conversejs.org
|
||||||
|
//
|
||||||
|
// Copyright (c) 2012-2017, Jan-Carel Brand <jc@opkode.com>
|
||||||
|
// Licensed under the Mozilla Public License (MPLv2)
|
||||||
|
//
|
||||||
|
/*global define */
|
||||||
|
|
||||||
|
(function (root, factory) {
|
||||||
|
define(["converse-core"], factory);
|
||||||
|
}(this, function (converse) {
|
||||||
|
"use strict";
|
||||||
|
const { Backbone, Strophe, b64_sha1, utils, _ } = converse.env;
|
||||||
|
|
||||||
|
converse.plugins.add('converse-chatboxes', {
|
||||||
|
|
||||||
|
overrides: {
|
||||||
|
// Overrides mentioned here will be picked up by converse.js's
|
||||||
|
// plugin architecture they will replace existing methods on the
|
||||||
|
// relevant objects or classes.
|
||||||
|
|
||||||
|
disconnect: function () {
|
||||||
|
const { _converse } = this.__super__;
|
||||||
|
_converse.chatboxviews.closeAllChatBoxes();
|
||||||
|
return this.__super__.disconnect.apply(this, arguments);
|
||||||
|
},
|
||||||
|
|
||||||
|
logOut: function () {
|
||||||
|
const { _converse } = this.__super__;
|
||||||
|
_converse.chatboxviews.closeAllChatBoxes();
|
||||||
|
return this.__super__.logOut.apply(this, arguments);
|
||||||
|
},
|
||||||
|
|
||||||
|
initStatus: function () {
|
||||||
|
const { _converse } = this.__super__;
|
||||||
|
_converse.chatboxviews.closeAllChatBoxes();
|
||||||
|
return this.__super__.initStatus.apply(this, arguments);
|
||||||
|
},
|
||||||
|
|
||||||
|
onStatusInitialized: function () {
|
||||||
|
const { _converse } = this.__super__;
|
||||||
|
_converse.chatboxes.onConnected();
|
||||||
|
return this.__super__.onStatusInitialized.apply(this, arguments);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
initialize () {
|
||||||
|
/* The initialize function gets called as soon as the plugin is
|
||||||
|
* loaded by converse.js's plugin machinery.
|
||||||
|
*/
|
||||||
|
const { _converse } = this;
|
||||||
|
|
||||||
|
_converse.api.promises.add([
|
||||||
|
'chatBoxesFetched',
|
||||||
|
'chatBoxesInitialized'
|
||||||
|
]);
|
||||||
|
|
||||||
|
_converse.ChatBoxes = Backbone.Collection.extend({
|
||||||
|
comparator: 'time_opened',
|
||||||
|
|
||||||
|
model (attrs, options) {
|
||||||
|
return new _converse.ChatBox(attrs, options);
|
||||||
|
},
|
||||||
|
|
||||||
|
registerMessageHandler () {
|
||||||
|
_converse.connection.addHandler(
|
||||||
|
this.onMessage.bind(this), null, 'message', 'chat'
|
||||||
|
);
|
||||||
|
_converse.connection.addHandler(
|
||||||
|
this.onErrorMessage.bind(this), null, 'message', 'error'
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
chatBoxMayBeShown (chatbox) {
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
|
||||||
|
onChatBoxesFetched (collection) {
|
||||||
|
/* Show chat boxes upon receiving them from sessionStorage
|
||||||
|
*
|
||||||
|
* This method gets overridden entirely in src/converse-controlbox.js
|
||||||
|
* if the controlbox plugin is active.
|
||||||
|
*/
|
||||||
|
collection.each((chatbox) => {
|
||||||
|
if (this.chatBoxMayBeShown(chatbox)) {
|
||||||
|
chatbox.trigger('show');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
_converse.emit('chatBoxesFetched');
|
||||||
|
},
|
||||||
|
|
||||||
|
onConnected () {
|
||||||
|
this.browserStorage = new Backbone.BrowserStorage[_converse.storage](
|
||||||
|
b64_sha1(`converse.chatboxes-${_converse.bare_jid}`));
|
||||||
|
this.registerMessageHandler();
|
||||||
|
this.fetch({
|
||||||
|
add: true,
|
||||||
|
success: this.onChatBoxesFetched.bind(this)
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
onErrorMessage (message) {
|
||||||
|
/* Handler method for all incoming error message stanzas
|
||||||
|
*/
|
||||||
|
// TODO: we can likely just reuse "onMessage" below
|
||||||
|
const from_jid = Strophe.getBareJidFromJid(message.getAttribute('from'));
|
||||||
|
if (utils.isSameBareJID(from_jid, _converse.bare_jid)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// Get chat box, but only create a new one when the message has a body.
|
||||||
|
const chatbox = this.getChatBox(from_jid);
|
||||||
|
if (!chatbox) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
chatbox.createMessage(message, null, message);
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
|
||||||
|
onMessage (message) {
|
||||||
|
/* Handler method for all incoming single-user chat "message"
|
||||||
|
* stanzas.
|
||||||
|
*/
|
||||||
|
let contact_jid, delay, resource,
|
||||||
|
from_jid = message.getAttribute('from'),
|
||||||
|
to_jid = message.getAttribute('to');
|
||||||
|
|
||||||
|
const original_stanza = message,
|
||||||
|
to_resource = Strophe.getResourceFromJid(to_jid),
|
||||||
|
is_carbon = !_.isNull(message.querySelector(`received[xmlns="${Strophe.NS.CARBONS}"]`));
|
||||||
|
|
||||||
|
if (_converse.filter_by_resource && (to_resource && to_resource !== _converse.resource)) {
|
||||||
|
_converse.log(
|
||||||
|
`onMessage: Ignoring incoming message intended for a different resource: ${to_jid}`,
|
||||||
|
Strophe.LogLevel.INFO
|
||||||
|
);
|
||||||
|
return true;
|
||||||
|
} else if (utils.isHeadlineMessage(message)) {
|
||||||
|
// XXX: Ideally we wouldn't have to check for headline
|
||||||
|
// messages, but Prosody sends headline messages with the
|
||||||
|
// wrong type ('chat'), so we need to filter them out here.
|
||||||
|
_converse.log(
|
||||||
|
`onMessage: Ignoring incoming headline message sent with type 'chat' from JID: ${from_jid}`,
|
||||||
|
Strophe.LogLevel.INFO
|
||||||
|
);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
const forwarded = message.querySelector('forwarded');
|
||||||
|
if (!_.isNull(forwarded)) {
|
||||||
|
const forwarded_message = forwarded.querySelector('message');
|
||||||
|
const forwarded_from = forwarded_message.getAttribute('from');
|
||||||
|
if (is_carbon && Strophe.getBareJidFromJid(forwarded_from) !== from_jid) {
|
||||||
|
// Prevent message forging via carbons
|
||||||
|
//
|
||||||
|
// https://xmpp.org/extensions/xep-0280.html#security
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
message = forwarded_message;
|
||||||
|
delay = forwarded.querySelector('delay');
|
||||||
|
from_jid = message.getAttribute('from');
|
||||||
|
to_jid = message.getAttribute('to');
|
||||||
|
}
|
||||||
|
|
||||||
|
const from_bare_jid = Strophe.getBareJidFromJid(from_jid),
|
||||||
|
from_resource = Strophe.getResourceFromJid(from_jid),
|
||||||
|
is_me = from_bare_jid === _converse.bare_jid;
|
||||||
|
|
||||||
|
if (is_me) {
|
||||||
|
// I am the sender, so this must be a forwarded message...
|
||||||
|
contact_jid = Strophe.getBareJidFromJid(to_jid);
|
||||||
|
resource = Strophe.getResourceFromJid(to_jid);
|
||||||
|
} else {
|
||||||
|
contact_jid = from_bare_jid;
|
||||||
|
resource = from_resource;
|
||||||
|
}
|
||||||
|
// Get chat box, but only create a new one when the message has a body.
|
||||||
|
const chatbox = this.getChatBox(contact_jid, !_.isNull(message.querySelector('body'))),
|
||||||
|
msgid = message.getAttribute('id');
|
||||||
|
|
||||||
|
if (chatbox) {
|
||||||
|
const messages = msgid && chatbox.messages.findWhere({msgid}) || [];
|
||||||
|
if (_.isEmpty(messages)) {
|
||||||
|
// Only create the message when we're sure it's not a
|
||||||
|
// duplicate
|
||||||
|
chatbox.incrementUnreadMsgCounter(original_stanza);
|
||||||
|
chatbox.createMessage(message, delay, original_stanza);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_converse.emit('message', {'stanza': original_stanza, 'chatbox': chatbox});
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
|
||||||
|
createChatBox (jid, attrs) {
|
||||||
|
/* Creates a chat box
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* (String) jid - The JID of the user for whom a chat box
|
||||||
|
* gets created.
|
||||||
|
* (Object) attrs - Optional chat box atributes.
|
||||||
|
*/
|
||||||
|
const bare_jid = Strophe.getBareJidFromJid(jid),
|
||||||
|
roster_item = _converse.roster.get(bare_jid);
|
||||||
|
let roster_info = {};
|
||||||
|
|
||||||
|
if (! _.isUndefined(roster_item)) {
|
||||||
|
roster_info = {
|
||||||
|
'fullname': _.isEmpty(roster_item.get('fullname'))? jid: roster_item.get('fullname'),
|
||||||
|
'image_type': roster_item.get('image_type'),
|
||||||
|
'image': roster_item.get('image'),
|
||||||
|
'url': roster_item.get('url'),
|
||||||
|
};
|
||||||
|
} else if (!_converse.allow_non_roster_messaging) {
|
||||||
|
_converse.log(`Could not get roster item for JID ${bare_jid}`+
|
||||||
|
' and allow_non_roster_messaging is set to false',
|
||||||
|
Strophe.LogLevel.ERROR);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
return this.create(_.assignIn({
|
||||||
|
'id': bare_jid,
|
||||||
|
'jid': bare_jid,
|
||||||
|
'fullname': jid,
|
||||||
|
'image_type': _converse.DEFAULT_IMAGE_TYPE,
|
||||||
|
'image': _converse.DEFAULT_IMAGE,
|
||||||
|
'url': '',
|
||||||
|
}, roster_info, attrs || {}));
|
||||||
|
},
|
||||||
|
|
||||||
|
getChatBox (jid, create, attrs) {
|
||||||
|
/* Returns a chat box or optionally return a newly
|
||||||
|
* created one if one doesn't exist.
|
||||||
|
*
|
||||||
|
* Parameters:
|
||||||
|
* (String) jid - The JID of the user whose chat box we want
|
||||||
|
* (Boolean) create - Should a new chat box be created if none exists?
|
||||||
|
* (Object) attrs - Optional chat box atributes.
|
||||||
|
*/
|
||||||
|
jid = jid.toLowerCase();
|
||||||
|
let chatbox = this.get(Strophe.getBareJidFromJid(jid));
|
||||||
|
if (!chatbox && create) {
|
||||||
|
chatbox = this.createChatBox(jid, attrs);
|
||||||
|
}
|
||||||
|
return chatbox;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
_converse.ChatBoxViews = Backbone.Overview.extend({
|
||||||
|
|
||||||
|
initialize () {
|
||||||
|
this.model.on("add", this.onChatBoxAdded, this);
|
||||||
|
this.model.on("destroy", this.removeChat, this);
|
||||||
|
},
|
||||||
|
|
||||||
|
_ensureElement () {
|
||||||
|
/* Override method from backbone.js
|
||||||
|
* If the #conversejs element doesn't exist, create it.
|
||||||
|
*/
|
||||||
|
if (!this.el) {
|
||||||
|
let el = document.querySelector('#conversejs');
|
||||||
|
if (_.isNull(el)) {
|
||||||
|
el = document.createElement('div');
|
||||||
|
el.setAttribute('id', 'conversejs');
|
||||||
|
// Converse.js expects a <body> tag to be present.
|
||||||
|
document.querySelector('body').appendChild(el);
|
||||||
|
}
|
||||||
|
el.innerHTML = '';
|
||||||
|
this.setElement(el, false);
|
||||||
|
} else {
|
||||||
|
this.setElement(_.result(this, 'el'), false);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
onChatBoxAdded (item) {
|
||||||
|
// Views aren't created here, since the core code doesn't
|
||||||
|
// contain any views. Instead, they're created in overrides in
|
||||||
|
// plugins, such as in converse-chatview.js and converse-muc.js
|
||||||
|
return this.get(item.get('id'));
|
||||||
|
},
|
||||||
|
|
||||||
|
removeChat (item) {
|
||||||
|
this.remove(item.get('id'));
|
||||||
|
},
|
||||||
|
|
||||||
|
closeAllChatBoxes () {
|
||||||
|
/* This method gets overridden in src/converse-controlbox.js if
|
||||||
|
* the controlbox plugin is active.
|
||||||
|
*/
|
||||||
|
this.each(function (view) { view.close(); });
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
|
||||||
|
chatBoxMayBeShown (chatbox) {
|
||||||
|
return this.model.chatBoxMayBeShown(chatbox);
|
||||||
|
},
|
||||||
|
|
||||||
|
getChatBox (attrs, create) {
|
||||||
|
let chatbox = this.model.get(attrs.jid);
|
||||||
|
if (!chatbox && create) {
|
||||||
|
chatbox = this.model.create(attrs, {
|
||||||
|
'error' (model, response) {
|
||||||
|
_converse.log(response.responseText);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return chatbox;
|
||||||
|
},
|
||||||
|
|
||||||
|
showChat (attrs) {
|
||||||
|
/* Find the chat box and show it (if it may be shown).
|
||||||
|
* If it doesn't exist, create it.
|
||||||
|
*/
|
||||||
|
const chatbox = this.getChatBox(attrs, true);
|
||||||
|
if (this.chatBoxMayBeShown(chatbox)) {
|
||||||
|
chatbox.trigger('show', true);
|
||||||
|
}
|
||||||
|
return chatbox;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// BEGIN: Event handlers
|
||||||
|
_converse.api.listen.on('pluginsInitialized', () => {
|
||||||
|
_converse.chatboxes = new _converse.ChatBoxes();
|
||||||
|
_converse.chatboxviews = new _converse.ChatBoxViews({
|
||||||
|
'model': _converse.chatboxes
|
||||||
|
});
|
||||||
|
_converse.emit('chatBoxesInitialized');
|
||||||
|
});
|
||||||
|
|
||||||
|
_converse.api.listen.on('beforeTearDown', () => {
|
||||||
|
_converse.chatboxes.remove(); // Don't call off(), events won't get re-registered upon reconnect.
|
||||||
|
delete _converse.chatboxes.browserStorage;
|
||||||
|
});
|
||||||
|
// END: Event handlers
|
||||||
|
|
||||||
|
_converse.getViewForChatBox = function (chatbox) {
|
||||||
|
if (!chatbox) { return; }
|
||||||
|
return _converse.chatboxviews.get(chatbox.get('id'));
|
||||||
|
};
|
||||||
|
|
||||||
|
/* We extend the default converse.js API */
|
||||||
|
_.extend(_converse.api, {
|
||||||
|
'chats': {
|
||||||
|
'open' (jids, attrs) {
|
||||||
|
if (_.isUndefined(jids)) {
|
||||||
|
_converse.log("chats.open: You need to provide at least one JID", Strophe.LogLevel.ERROR);
|
||||||
|
return null;
|
||||||
|
} else if (_.isString(jids)) {
|
||||||
|
return _converse.getViewForChatBox(
|
||||||
|
_converse.chatboxes.getChatBox(jids, true, attrs).trigger('show')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return _.map(jids, (jid) =>
|
||||||
|
_converse.getViewForChatBox(
|
||||||
|
_converse.chatboxes.getChatBox(jid, true, attrs).trigger('show')
|
||||||
|
)
|
||||||
|
);
|
||||||
|
},
|
||||||
|
'get' (jids) {
|
||||||
|
if (_.isUndefined(jids)) {
|
||||||
|
const result = [];
|
||||||
|
_converse.chatboxes.each(function (chatbox) {
|
||||||
|
// FIXME: Leaky abstraction from MUC. We need to add a
|
||||||
|
// base type for chat boxes, and check for that.
|
||||||
|
if (chatbox.get('type') !== 'chatroom') {
|
||||||
|
result.push(_converse.getViewForChatBox(chatbox));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
} else if (_.isString(jids)) {
|
||||||
|
return _converse.getViewForChatBox(_converse.chatboxes.getChatBox(jids));
|
||||||
|
}
|
||||||
|
return _.map(jids,
|
||||||
|
_.partial(
|
||||||
|
_.flow(
|
||||||
|
_converse.chatboxes.getChatBox.bind(_converse.chatboxes),
|
||||||
|
_converse.getViewForChatBox.bind(_converse)
|
||||||
|
), _, true
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return converse;
|
||||||
|
}));
|
@ -10,6 +10,7 @@
|
|||||||
define([
|
define([
|
||||||
"jquery.noconflict",
|
"jquery.noconflict",
|
||||||
"converse-core",
|
"converse-core",
|
||||||
|
"converse-chatboxes",
|
||||||
"emojione",
|
"emojione",
|
||||||
"xss",
|
"xss",
|
||||||
"tpl!chatbox",
|
"tpl!chatbox",
|
||||||
@ -25,6 +26,7 @@
|
|||||||
}(this, function (
|
}(this, function (
|
||||||
$,
|
$,
|
||||||
converse,
|
converse,
|
||||||
|
dummy,
|
||||||
emojione,
|
emojione,
|
||||||
xss,
|
xss,
|
||||||
tpl_chatbox,
|
tpl_chatbox,
|
||||||
@ -706,10 +708,10 @@
|
|||||||
}
|
}
|
||||||
if (state === _converse.COMPOSING) {
|
if (state === _converse.COMPOSING) {
|
||||||
this.chat_state_timeout = window.setTimeout(
|
this.chat_state_timeout = window.setTimeout(
|
||||||
this.setChatState.bind(this), _converse.TIMEOUTS.PAUSED, _converse.PAUSED);
|
this.setChatState.bind(this), _converse.TIMEOUTS.PAUSED, _converse.PAUSED);
|
||||||
} else if (state === _converse.PAUSED) {
|
} else if (state === _converse.PAUSED) {
|
||||||
this.chat_state_timeout = window.setTimeout(
|
this.chat_state_timeout = window.setTimeout(
|
||||||
this.setChatState.bind(this), _converse.TIMEOUTS.INACTIVE, _converse.INACTIVE);
|
this.setChatState.bind(this), _converse.TIMEOUTS.INACTIVE, _converse.INACTIVE);
|
||||||
}
|
}
|
||||||
if (!no_save && this.model.get('chat_state') !== state) {
|
if (!no_save && this.model.get('chat_state') !== state) {
|
||||||
this.model.set('chat_state', state);
|
this.model.set('chat_state', state);
|
||||||
|
@ -48,7 +48,7 @@
|
|||||||
|
|
||||||
const USERS_PANEL_ID = 'users';
|
const USERS_PANEL_ID = 'users';
|
||||||
const CHATBOX_TYPE = 'chatbox';
|
const CHATBOX_TYPE = 'chatbox';
|
||||||
const { Strophe, Backbone, utils, _, moment } = converse.env;
|
const { Strophe, Backbone, Promise, utils, _, moment } = converse.env;
|
||||||
|
|
||||||
|
|
||||||
converse.plugins.add('converse-controlbox', {
|
converse.plugins.add('converse-controlbox', {
|
||||||
@ -60,18 +60,6 @@
|
|||||||
//
|
//
|
||||||
// New functions which don't exist yet can also be added.
|
// New functions which don't exist yet can also be added.
|
||||||
|
|
||||||
initChatBoxes () {
|
|
||||||
this.__super__.initChatBoxes.apply(this, arguments);
|
|
||||||
this.controlboxtoggle = new this.ControlBoxToggle();
|
|
||||||
},
|
|
||||||
|
|
||||||
initConnection () {
|
|
||||||
this.__super__.initConnection.apply(this, arguments);
|
|
||||||
if (this.connection) {
|
|
||||||
this.addControlBox();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
_tearDown () {
|
_tearDown () {
|
||||||
this.__super__._tearDown.apply(this, arguments);
|
this.__super__._tearDown.apply(this, arguments);
|
||||||
if (this.rosterview) {
|
if (this.rosterview) {
|
||||||
@ -102,8 +90,8 @@
|
|||||||
},
|
},
|
||||||
|
|
||||||
onChatBoxesFetched (collection, resp) {
|
onChatBoxesFetched (collection, resp) {
|
||||||
const { _converse } = this.__super__;
|
|
||||||
this.__super__.onChatBoxesFetched.apply(this, arguments);
|
this.__super__.onChatBoxesFetched.apply(this, arguments);
|
||||||
|
const { _converse } = this.__super__;
|
||||||
if (!_.includes(_.map(collection, 'id'), 'controlbox')) {
|
if (!_.includes(_.map(collection, 'id'), 'controlbox')) {
|
||||||
_converse.addControlBox();
|
_converse.addControlBox();
|
||||||
}
|
}
|
||||||
@ -159,7 +147,6 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
ChatBox: {
|
ChatBox: {
|
||||||
initialize () {
|
initialize () {
|
||||||
if (this.get('id') === 'controlbox') {
|
if (this.get('id') === 'controlbox') {
|
||||||
@ -170,7 +157,6 @@
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
ChatBoxView: {
|
ChatBoxView: {
|
||||||
insertIntoDOM () {
|
insertIntoDOM () {
|
||||||
const { _converse } = this.__super__;
|
const { _converse } = this.__super__;
|
||||||
@ -199,14 +185,14 @@
|
|||||||
|
|
||||||
const LABEL_CONTACTS = __('Contacts');
|
const LABEL_CONTACTS = __('Contacts');
|
||||||
|
|
||||||
_converse.addControlBox = () =>
|
_converse.addControlBox = () => {
|
||||||
_converse.chatboxes.add({
|
_converse.chatboxes.add({
|
||||||
id: 'controlbox',
|
id: 'controlbox',
|
||||||
box_id: 'controlbox',
|
box_id: 'controlbox',
|
||||||
type: 'controlbox',
|
type: 'controlbox',
|
||||||
closed: !_converse.show_controlbox_by_default
|
closed: !_converse.show_controlbox_by_default
|
||||||
})
|
})
|
||||||
;
|
};
|
||||||
|
|
||||||
_converse.ControlBoxView = _converse.ChatBoxView.extend({
|
_converse.ControlBoxView = _converse.ChatBoxView.extend({
|
||||||
tagName: 'div',
|
tagName: 'div',
|
||||||
@ -218,6 +204,7 @@
|
|||||||
},
|
},
|
||||||
|
|
||||||
initialize () {
|
initialize () {
|
||||||
|
_converse.controlboxtoggle = new _converse.ControlBoxToggle();
|
||||||
this.$el.insertAfter(_converse.controlboxtoggle.$el);
|
this.$el.insertAfter(_converse.controlboxtoggle.$el);
|
||||||
this.model.on('change:connected', this.onConnected, this);
|
this.model.on('change:connected', this.onConnected, this);
|
||||||
this.model.on('destroy', this.hide, this);
|
this.model.on('destroy', this.hide, this);
|
||||||
@ -226,7 +213,9 @@
|
|||||||
this.model.on('change:closed', this.ensureClosedState, this);
|
this.model.on('change:closed', this.ensureClosedState, this);
|
||||||
this.render();
|
this.render();
|
||||||
if (this.model.get('connected')) {
|
if (this.model.get('connected')) {
|
||||||
this.insertRoster();
|
_converse.api.waitUntil('rosterViewInitialized')
|
||||||
|
.then(this.insertRoster.bind(this))
|
||||||
|
.catch(_.partial(_converse.log, _, Strophe.LogLevel.FATAL));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -259,7 +248,10 @@
|
|||||||
|
|
||||||
onConnected () {
|
onConnected () {
|
||||||
if (this.model.get('connected')) {
|
if (this.model.get('connected')) {
|
||||||
this.render().insertRoster();
|
this.render();
|
||||||
|
_converse.api.waitUntil('rosterViewInitialized')
|
||||||
|
.then(this.insertRoster.bind(this))
|
||||||
|
.catch(_.partial(_converse.log, _, Strophe.LogLevel.FATAL));
|
||||||
this.model.save();
|
this.model.save();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -735,15 +727,15 @@
|
|||||||
},
|
},
|
||||||
|
|
||||||
initialize () {
|
initialize () {
|
||||||
_converse.chatboxviews.$el.prepend(this.render());
|
_converse.chatboxviews.$el.prepend(this.render().el);
|
||||||
this.updateOnlineCount();
|
this.updateOnlineCount();
|
||||||
const that = this;
|
const that = this;
|
||||||
_converse.on('initialized', function () {
|
_converse.api.waitUntil('initialized').then(() => {
|
||||||
_converse.roster.on("add", that.updateOnlineCount, that);
|
_converse.roster.on("add", that.updateOnlineCount, that);
|
||||||
_converse.roster.on('change', that.updateOnlineCount, that);
|
_converse.roster.on('change', that.updateOnlineCount, that);
|
||||||
_converse.roster.on("destroy", that.updateOnlineCount, that);
|
_converse.roster.on("destroy", that.updateOnlineCount, that);
|
||||||
_converse.roster.on("remove", that.updateOnlineCount, that);
|
_converse.roster.on("remove", that.updateOnlineCount, that);
|
||||||
});
|
}).catch(_.partial(_converse.log, _, Strophe.LogLevel.FATAL));
|
||||||
},
|
},
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
@ -751,11 +743,10 @@
|
|||||||
// the ControlBox or the Toggle must be shown. This prevents
|
// the ControlBox or the Toggle must be shown. This prevents
|
||||||
// artifacts (i.e. on page load the toggle is shown only to then
|
// artifacts (i.e. on page load the toggle is shown only to then
|
||||||
// seconds later be hidden in favor of the control box).
|
// seconds later be hidden in favor of the control box).
|
||||||
return this.$el.html(
|
this.el.innerHTML = tpl_controlbox_toggle({
|
||||||
tpl_controlbox_toggle({
|
'label_toggle': __('Toggle chat')
|
||||||
'label_toggle': __('Toggle chat')
|
})
|
||||||
})
|
return this;
|
||||||
);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
updateOnlineCount: _.debounce(function () {
|
updateOnlineCount: _.debounce(function () {
|
||||||
@ -805,6 +796,12 @@
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Promise.all([
|
||||||
|
_converse.api.waitUntil('connectionInitialized'),
|
||||||
|
_converse.api.waitUntil('chatBoxesInitialized')
|
||||||
|
]).then(_converse.addControlBox)
|
||||||
|
.catch(_.partial(_converse.log, _, Strophe.LogLevel.FATAL));
|
||||||
|
|
||||||
const disconnect = function () {
|
const disconnect = function () {
|
||||||
/* Upon disconnection, set connected to `false`, so that if
|
/* Upon disconnection, set connected to `false`, so that if
|
||||||
* we reconnect,
|
* we reconnect,
|
||||||
|
@ -51,6 +51,7 @@
|
|||||||
|
|
||||||
_converse.core_plugins = [
|
_converse.core_plugins = [
|
||||||
'converse-bookmarks',
|
'converse-bookmarks',
|
||||||
|
'converse-chatboxes',
|
||||||
'converse-chatview',
|
'converse-chatview',
|
||||||
'converse-controlbox',
|
'converse-controlbox',
|
||||||
'converse-core',
|
'converse-core',
|
||||||
@ -112,8 +113,8 @@
|
|||||||
9: 'REDIRECT'
|
9: 'REDIRECT'
|
||||||
};
|
};
|
||||||
|
|
||||||
const DEFAULT_IMAGE_TYPE = 'image/png';
|
_converse.DEFAULT_IMAGE_TYPE = 'image/png';
|
||||||
const DEFAULT_IMAGE = "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";
|
_converse.DEFAULT_IMAGE = "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";
|
||||||
|
|
||||||
_converse.log = function (message, level) {
|
_converse.log = function (message, level) {
|
||||||
if (message instanceof Error) {
|
if (message instanceof Error) {
|
||||||
@ -149,8 +150,9 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
const PROMISES = [
|
const PROMISES = [
|
||||||
|
'initialized',
|
||||||
'cachedRoster',
|
'cachedRoster',
|
||||||
'chatBoxesFetched',
|
'connectionInitialized',
|
||||||
'pluginsInitialized',
|
'pluginsInitialized',
|
||||||
'roster',
|
'roster',
|
||||||
'rosterContactsFetched',
|
'rosterContactsFetched',
|
||||||
@ -182,7 +184,7 @@
|
|||||||
|
|
||||||
_.each(PROMISES, addPromise);
|
_.each(PROMISES, addPromise);
|
||||||
|
|
||||||
if (!_.isUndefined(_converse.chatboxes)) {
|
if (!_.isUndefined(_converse.connection)) {
|
||||||
// Looks like _converse.initialized was called again without logging
|
// Looks like _converse.initialized was called again without logging
|
||||||
// out or disconnecting in the previous session.
|
// out or disconnecting in the previous session.
|
||||||
// This happens in tests. We therefore first clean up.
|
// This happens in tests. We therefore first clean up.
|
||||||
@ -320,10 +322,6 @@
|
|||||||
|
|
||||||
// Module-level functions
|
// Module-level functions
|
||||||
// ----------------------
|
// ----------------------
|
||||||
this.getViewForChatBox = function (chatbox) {
|
|
||||||
if (!chatbox) { return; }
|
|
||||||
return _converse.chatboxviews.get(chatbox.get('id'));
|
|
||||||
};
|
|
||||||
|
|
||||||
this.generateResource = () => `/converse.js-${Math.floor(Math.random()*139749825).toString()}`;
|
this.generateResource = () => `/converse.js-${Math.floor(Math.random()*139749825).toString()}`;
|
||||||
|
|
||||||
@ -455,7 +453,6 @@
|
|||||||
delete _converse.connection.reconnecting;
|
delete _converse.connection.reconnecting;
|
||||||
_converse.connection.reset();
|
_converse.connection.reset();
|
||||||
_converse._tearDown();
|
_converse._tearDown();
|
||||||
_converse.chatboxviews.closeAllChatBoxes();
|
|
||||||
_converse.emit('disconnected');
|
_converse.emit('disconnected');
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -579,11 +576,11 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
this.initSession = function () {
|
this.initSession = function () {
|
||||||
this.session = new Backbone.Model();
|
_converse.session = new Backbone.Model();
|
||||||
const id = b64_sha1('converse.bosh-session');
|
const id = b64_sha1('converse.bosh-session');
|
||||||
this.session.id = id; // Appears to be necessary for backbone.browserStorage
|
_converse.session.id = id; // Appears to be necessary for backbone.browserStorage
|
||||||
this.session.browserStorage = new Backbone.BrowserStorage[_converse.storage](id);
|
_converse.session.browserStorage = new Backbone.BrowserStorage[_converse.storage](id);
|
||||||
this.session.fetch();
|
_converse.session.fetch();
|
||||||
};
|
};
|
||||||
|
|
||||||
this.clearSession = function () {
|
this.clearSession = function () {
|
||||||
@ -596,9 +593,7 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
this.logOut = function () {
|
this.logOut = function () {
|
||||||
_converse.chatboxviews.closeAllChatBoxes();
|
|
||||||
_converse.clearSession();
|
_converse.clearSession();
|
||||||
|
|
||||||
_converse.setDisconnectionCause(_converse.LOGOUT, undefined, true);
|
_converse.setDisconnectionCause(_converse.LOGOUT, undefined, true);
|
||||||
if (!_.isUndefined(_converse.connection)) {
|
if (!_.isUndefined(_converse.connection)) {
|
||||||
_converse.connection.disconnect();
|
_converse.connection.disconnect();
|
||||||
@ -750,10 +745,7 @@
|
|||||||
_converse.registerIntervalHandler();
|
_converse.registerIntervalHandler();
|
||||||
_converse.initRoster();
|
_converse.initRoster();
|
||||||
}
|
}
|
||||||
// First set up chat boxes, before populating the roster, so that
|
|
||||||
// the controlbox is properly set up and ready for the rosterview.
|
|
||||||
_converse.roster.onConnected();
|
_converse.roster.onConnected();
|
||||||
_converse.chatboxes.onConnected();
|
|
||||||
_converse.populateRoster();
|
_converse.populateRoster();
|
||||||
_converse.registerPresenceHandler();
|
_converse.registerPresenceHandler();
|
||||||
_converse.giveFeedback(__('Contacts'));
|
_converse.giveFeedback(__('Contacts'));
|
||||||
@ -791,7 +783,6 @@
|
|||||||
_converse.onStatusInitialized(true);
|
_converse.onStatusInitialized(true);
|
||||||
_converse.emit('reconnected');
|
_converse.emit('reconnected');
|
||||||
} else {
|
} else {
|
||||||
_converse.chatboxviews.closeAllChatBoxes();
|
|
||||||
_converse.initStatus()
|
_converse.initStatus()
|
||||||
.then(
|
.then(
|
||||||
_.partial(_converse.onStatusInitialized, false),
|
_.partial(_converse.onStatusInitialized, false),
|
||||||
@ -808,8 +799,8 @@
|
|||||||
'chat_state': undefined,
|
'chat_state': undefined,
|
||||||
'chat_status': 'offline',
|
'chat_status': 'offline',
|
||||||
'groups': [],
|
'groups': [],
|
||||||
'image': DEFAULT_IMAGE,
|
'image': _converse.DEFAULT_IMAGE,
|
||||||
'image_type': DEFAULT_IMAGE_TYPE,
|
'image_type': _converse.DEFAULT_IMAGE_TYPE,
|
||||||
'num_unread': 0,
|
'num_unread': 0,
|
||||||
'status': '',
|
'status': '',
|
||||||
},
|
},
|
||||||
@ -1519,267 +1510,6 @@
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
this.ChatBoxes = Backbone.Collection.extend({
|
|
||||||
comparator: 'time_opened',
|
|
||||||
|
|
||||||
model (attrs, options) {
|
|
||||||
return new _converse.ChatBox(attrs, options);
|
|
||||||
},
|
|
||||||
|
|
||||||
registerMessageHandler () {
|
|
||||||
_converse.connection.addHandler(
|
|
||||||
this.onMessage.bind(this), null, 'message', 'chat'
|
|
||||||
);
|
|
||||||
_converse.connection.addHandler(
|
|
||||||
this.onErrorMessage.bind(this), null, 'message', 'error'
|
|
||||||
);
|
|
||||||
},
|
|
||||||
|
|
||||||
chatBoxMayBeShown (chatbox) {
|
|
||||||
return true;
|
|
||||||
},
|
|
||||||
|
|
||||||
onChatBoxesFetched (collection) {
|
|
||||||
/* Show chat boxes upon receiving them from sessionStorage
|
|
||||||
*
|
|
||||||
* This method gets overridden entirely in src/converse-controlbox.js
|
|
||||||
* if the controlbox plugin is active.
|
|
||||||
*/
|
|
||||||
collection.each((chatbox) => {
|
|
||||||
if (this.chatBoxMayBeShown(chatbox)) {
|
|
||||||
chatbox.trigger('show');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
_converse.emit('chatBoxesFetched');
|
|
||||||
},
|
|
||||||
|
|
||||||
onConnected () {
|
|
||||||
this.browserStorage = new Backbone.BrowserStorage[_converse.storage](
|
|
||||||
b64_sha1(`converse.chatboxes-${_converse.bare_jid}`));
|
|
||||||
this.registerMessageHandler();
|
|
||||||
this.fetch({
|
|
||||||
add: true,
|
|
||||||
success: this.onChatBoxesFetched.bind(this)
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
onErrorMessage (message) {
|
|
||||||
/* Handler method for all incoming error message stanzas
|
|
||||||
*/
|
|
||||||
// TODO: we can likely just reuse "onMessage" below
|
|
||||||
const from_jid = Strophe.getBareJidFromJid(message.getAttribute('from'));
|
|
||||||
if (utils.isSameBareJID(from_jid, _converse.bare_jid)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
// Get chat box, but only create a new one when the message has a body.
|
|
||||||
const chatbox = this.getChatBox(from_jid);
|
|
||||||
if (!chatbox) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
chatbox.createMessage(message, null, message);
|
|
||||||
return true;
|
|
||||||
},
|
|
||||||
|
|
||||||
onMessage (message) {
|
|
||||||
/* Handler method for all incoming single-user chat "message"
|
|
||||||
* stanzas.
|
|
||||||
*/
|
|
||||||
let contact_jid, delay, resource,
|
|
||||||
from_jid = message.getAttribute('from'),
|
|
||||||
to_jid = message.getAttribute('to');
|
|
||||||
|
|
||||||
const original_stanza = message,
|
|
||||||
to_resource = Strophe.getResourceFromJid(to_jid),
|
|
||||||
is_carbon = !_.isNull(message.querySelector(`received[xmlns="${Strophe.NS.CARBONS}"]`));
|
|
||||||
|
|
||||||
if (_converse.filter_by_resource && (to_resource && to_resource !== _converse.resource)) {
|
|
||||||
_converse.log(
|
|
||||||
`onMessage: Ignoring incoming message intended for a different resource: ${to_jid}`,
|
|
||||||
Strophe.LogLevel.INFO
|
|
||||||
);
|
|
||||||
return true;
|
|
||||||
} else if (utils.isHeadlineMessage(message)) {
|
|
||||||
// XXX: Ideally we wouldn't have to check for headline
|
|
||||||
// messages, but Prosody sends headline messages with the
|
|
||||||
// wrong type ('chat'), so we need to filter them out here.
|
|
||||||
_converse.log(
|
|
||||||
`onMessage: Ignoring incoming headline message sent with type 'chat' from JID: ${from_jid}`,
|
|
||||||
Strophe.LogLevel.INFO
|
|
||||||
);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
const forwarded = message.querySelector('forwarded');
|
|
||||||
if (!_.isNull(forwarded)) {
|
|
||||||
const forwarded_message = forwarded.querySelector('message');
|
|
||||||
const forwarded_from = forwarded_message.getAttribute('from');
|
|
||||||
if (is_carbon && Strophe.getBareJidFromJid(forwarded_from) !== from_jid) {
|
|
||||||
// Prevent message forging via carbons
|
|
||||||
//
|
|
||||||
// https://xmpp.org/extensions/xep-0280.html#security
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
message = forwarded_message;
|
|
||||||
delay = forwarded.querySelector('delay');
|
|
||||||
from_jid = message.getAttribute('from');
|
|
||||||
to_jid = message.getAttribute('to');
|
|
||||||
}
|
|
||||||
|
|
||||||
const from_bare_jid = Strophe.getBareJidFromJid(from_jid),
|
|
||||||
from_resource = Strophe.getResourceFromJid(from_jid),
|
|
||||||
is_me = from_bare_jid === _converse.bare_jid;
|
|
||||||
|
|
||||||
if (is_me) {
|
|
||||||
// I am the sender, so this must be a forwarded message...
|
|
||||||
contact_jid = Strophe.getBareJidFromJid(to_jid);
|
|
||||||
resource = Strophe.getResourceFromJid(to_jid);
|
|
||||||
} else {
|
|
||||||
contact_jid = from_bare_jid;
|
|
||||||
resource = from_resource;
|
|
||||||
}
|
|
||||||
// Get chat box, but only create a new one when the message has a body.
|
|
||||||
const chatbox = this.getChatBox(contact_jid, !_.isNull(message.querySelector('body'))),
|
|
||||||
msgid = message.getAttribute('id');
|
|
||||||
|
|
||||||
if (chatbox) {
|
|
||||||
const messages = msgid && chatbox.messages.findWhere({msgid}) || [];
|
|
||||||
if (_.isEmpty(messages)) {
|
|
||||||
// Only create the message when we're sure it's not a
|
|
||||||
// duplicate
|
|
||||||
chatbox.incrementUnreadMsgCounter(original_stanza);
|
|
||||||
chatbox.createMessage(message, delay, original_stanza);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_converse.emit('message', {'stanza': original_stanza, 'chatbox': chatbox});
|
|
||||||
return true;
|
|
||||||
},
|
|
||||||
|
|
||||||
createChatBox (jid, attrs) {
|
|
||||||
/* Creates a chat box
|
|
||||||
*
|
|
||||||
* Parameters:
|
|
||||||
* (String) jid - The JID of the user for whom a chat box
|
|
||||||
* gets created.
|
|
||||||
* (Object) attrs - Optional chat box atributes.
|
|
||||||
*/
|
|
||||||
const bare_jid = Strophe.getBareJidFromJid(jid),
|
|
||||||
roster_item = _converse.roster.get(bare_jid);
|
|
||||||
let roster_info = {};
|
|
||||||
|
|
||||||
if (! _.isUndefined(roster_item)) {
|
|
||||||
roster_info = {
|
|
||||||
'fullname': _.isEmpty(roster_item.get('fullname'))? jid: roster_item.get('fullname'),
|
|
||||||
'image_type': roster_item.get('image_type'),
|
|
||||||
'image': roster_item.get('image'),
|
|
||||||
'url': roster_item.get('url'),
|
|
||||||
};
|
|
||||||
} else if (!_converse.allow_non_roster_messaging) {
|
|
||||||
_converse.log(`Could not get roster item for JID ${bare_jid}`+
|
|
||||||
' and allow_non_roster_messaging is set to false',
|
|
||||||
Strophe.LogLevel.ERROR);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
return this.create(_.assignIn({
|
|
||||||
'id': bare_jid,
|
|
||||||
'jid': bare_jid,
|
|
||||||
'fullname': jid,
|
|
||||||
'image_type': DEFAULT_IMAGE_TYPE,
|
|
||||||
'image': DEFAULT_IMAGE,
|
|
||||||
'url': '',
|
|
||||||
}, roster_info, attrs || {}));
|
|
||||||
},
|
|
||||||
|
|
||||||
getChatBox (jid, create, attrs) {
|
|
||||||
/* Returns a chat box or optionally return a newly
|
|
||||||
* created one if one doesn't exist.
|
|
||||||
*
|
|
||||||
* Parameters:
|
|
||||||
* (String) jid - The JID of the user whose chat box we want
|
|
||||||
* (Boolean) create - Should a new chat box be created if none exists?
|
|
||||||
* (Object) attrs - Optional chat box atributes.
|
|
||||||
*/
|
|
||||||
jid = jid.toLowerCase();
|
|
||||||
let chatbox = this.get(Strophe.getBareJidFromJid(jid));
|
|
||||||
if (!chatbox && create) {
|
|
||||||
chatbox = this.createChatBox(jid, attrs);
|
|
||||||
}
|
|
||||||
return chatbox;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
this.ChatBoxViews = Backbone.Overview.extend({
|
|
||||||
|
|
||||||
initialize () {
|
|
||||||
this.model.on("add", this.onChatBoxAdded, this);
|
|
||||||
this.model.on("destroy", this.removeChat, this);
|
|
||||||
},
|
|
||||||
|
|
||||||
_ensureElement () {
|
|
||||||
/* Override method from backbone.js
|
|
||||||
* If the #conversejs element doesn't exist, create it.
|
|
||||||
*/
|
|
||||||
if (!this.el) {
|
|
||||||
let el = document.querySelector('#conversejs');
|
|
||||||
if (_.isNull(el)) {
|
|
||||||
el = document.createElement('div');
|
|
||||||
el.setAttribute('id', 'conversejs');
|
|
||||||
// Converse.js expects a <body> tag to be present.
|
|
||||||
document.querySelector('body').appendChild(el);
|
|
||||||
}
|
|
||||||
el.innerHTML = '';
|
|
||||||
this.setElement(el, false);
|
|
||||||
} else {
|
|
||||||
this.setElement(_.result(this, 'el'), false);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
onChatBoxAdded (item) {
|
|
||||||
// Views aren't created here, since the core code doesn't
|
|
||||||
// contain any views. Instead, they're created in overrides in
|
|
||||||
// plugins, such as in converse-chatview.js and converse-muc.js
|
|
||||||
return this.get(item.get('id'));
|
|
||||||
},
|
|
||||||
|
|
||||||
removeChat (item) {
|
|
||||||
this.remove(item.get('id'));
|
|
||||||
},
|
|
||||||
|
|
||||||
closeAllChatBoxes () {
|
|
||||||
/* This method gets overridden in src/converse-controlbox.js if
|
|
||||||
* the controlbox plugin is active.
|
|
||||||
*/
|
|
||||||
this.each(function (view) { view.close(); });
|
|
||||||
return this;
|
|
||||||
},
|
|
||||||
|
|
||||||
chatBoxMayBeShown (chatbox) {
|
|
||||||
return this.model.chatBoxMayBeShown(chatbox);
|
|
||||||
},
|
|
||||||
|
|
||||||
getChatBox (attrs, create) {
|
|
||||||
let chatbox = this.model.get(attrs.jid);
|
|
||||||
if (!chatbox && create) {
|
|
||||||
chatbox = this.model.create(attrs, {
|
|
||||||
'error' (model, response) {
|
|
||||||
_converse.log(response.responseText);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return chatbox;
|
|
||||||
},
|
|
||||||
|
|
||||||
showChat (attrs) {
|
|
||||||
/* Find the chat box and show it (if it may be shown).
|
|
||||||
* If it doesn't exist, create it.
|
|
||||||
*/
|
|
||||||
const chatbox = this.getChatBox(attrs, true);
|
|
||||||
if (this.chatBoxMayBeShown(chatbox)) {
|
|
||||||
chatbox.trigger('show', true);
|
|
||||||
}
|
|
||||||
return chatbox;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
this.XMPPStatus = Backbone.Model.extend({
|
this.XMPPStatus = Backbone.Model.extend({
|
||||||
initialize () {
|
initialize () {
|
||||||
this.set({
|
this.set({
|
||||||
@ -1886,7 +1616,7 @@
|
|||||||
};
|
};
|
||||||
xhr.onerror = function () {
|
xhr.onerror = function () {
|
||||||
delete _converse.connection;
|
delete _converse.connection;
|
||||||
_converse.emit('noResumeableSession');
|
_converse.emit('noResumeableSession', this);
|
||||||
reject(xhr.responseText);
|
reject(xhr.responseText);
|
||||||
};
|
};
|
||||||
xhr.send();
|
xhr.send();
|
||||||
@ -1908,7 +1638,7 @@
|
|||||||
};
|
};
|
||||||
xhr.onerror = function () {
|
xhr.onerror = function () {
|
||||||
delete _converse.connection;
|
delete _converse.connection;
|
||||||
_converse.emit('noResumeableSession');
|
_converse.emit('noResumeableSession', this);
|
||||||
};
|
};
|
||||||
xhr.send();
|
xhr.send();
|
||||||
};
|
};
|
||||||
@ -2049,22 +1779,22 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
this.initConnection = function () {
|
this.initConnection = function () {
|
||||||
if (this.connection) {
|
if (!this.connection) {
|
||||||
return;
|
if (!this.bosh_service_url && ! this.websocket_url) {
|
||||||
}
|
throw new Error("initConnection: you must supply a value for either the bosh_service_url or websocket_url or both.");
|
||||||
if (!this.bosh_service_url && ! this.websocket_url) {
|
}
|
||||||
throw new Error("initConnection: you must supply a value for either the bosh_service_url or websocket_url or both.");
|
if (('WebSocket' in window || 'MozWebSocket' in window) && this.websocket_url) {
|
||||||
}
|
this.connection = new Strophe.Connection(this.websocket_url, this.connection_options);
|
||||||
if (('WebSocket' in window || 'MozWebSocket' in window) && this.websocket_url) {
|
} else if (this.bosh_service_url) {
|
||||||
this.connection = new Strophe.Connection(this.websocket_url, this.connection_options);
|
this.connection = new Strophe.Connection(
|
||||||
} else if (this.bosh_service_url) {
|
this.bosh_service_url,
|
||||||
this.connection = new Strophe.Connection(
|
_.assignIn(this.connection_options, {'keepalive': this.keepalive})
|
||||||
this.bosh_service_url,
|
);
|
||||||
_.assignIn(this.connection_options, {'keepalive': this.keepalive})
|
} else {
|
||||||
);
|
throw new Error("initConnection: this browser does not support websockets and bosh_service_url wasn't specified.");
|
||||||
} else {
|
}
|
||||||
throw new Error("initConnection: this browser does not support websockets and bosh_service_url wasn't specified.");
|
|
||||||
}
|
}
|
||||||
|
_converse.emit('connectionInitialized');
|
||||||
};
|
};
|
||||||
|
|
||||||
this._tearDown = function () {
|
this._tearDown = function () {
|
||||||
@ -2072,13 +1802,13 @@
|
|||||||
* connection.
|
* connection.
|
||||||
*/
|
*/
|
||||||
_converse.emit('beforeTearDown');
|
_converse.emit('beforeTearDown');
|
||||||
this.unregisterPresenceHandler();
|
_converse.unregisterPresenceHandler();
|
||||||
if (this.roster) {
|
if (_converse.roster) {
|
||||||
this.roster.off().reset(); // Removes roster contacts
|
_converse.roster.off().reset(); // Removes roster contacts
|
||||||
|
}
|
||||||
|
if (!_.isUndefined(_converse.session)) {
|
||||||
|
_converse.session.destroy();
|
||||||
}
|
}
|
||||||
this.chatboxes.remove(); // Don't call off(), events won't get re-registered upon reconnect.
|
|
||||||
delete this.chatboxes.browserStorage;
|
|
||||||
this.session.destroy();
|
|
||||||
window.removeEventListener('click', _converse.onUserActivity);
|
window.removeEventListener('click', _converse.onUserActivity);
|
||||||
window.removeEventListener('focus', _converse.onUserActivity);
|
window.removeEventListener('focus', _converse.onUserActivity);
|
||||||
window.removeEventListener('keypress', _converse.onUserActivity);
|
window.removeEventListener('keypress', _converse.onUserActivity);
|
||||||
@ -2086,12 +1816,7 @@
|
|||||||
window.removeEventListener(unloadevent, _converse.onUserActivity);
|
window.removeEventListener(unloadevent, _converse.onUserActivity);
|
||||||
window.clearInterval(_converse.everySecondTrigger);
|
window.clearInterval(_converse.everySecondTrigger);
|
||||||
_converse.emit('afterTearDown');
|
_converse.emit('afterTearDown');
|
||||||
return this;
|
return _converse;
|
||||||
};
|
|
||||||
|
|
||||||
this.initChatBoxes = function () {
|
|
||||||
this.chatboxes = new this.ChatBoxes();
|
|
||||||
this.chatboxviews = new this.ChatBoxViews({model: this.chatboxes});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
this.initPlugins = function () {
|
this.initPlugins = function () {
|
||||||
@ -2127,7 +1852,6 @@
|
|||||||
this.connection = settings.connection;
|
this.connection = settings.connection;
|
||||||
}
|
}
|
||||||
_converse.initPlugins();
|
_converse.initPlugins();
|
||||||
_converse.initChatBoxes();
|
|
||||||
_converse.initConnection();
|
_converse.initConnection();
|
||||||
_converse.setUpXMLLogging();
|
_converse.setUpXMLLogging();
|
||||||
_converse.logIn();
|
_converse.logIn();
|
||||||
@ -2240,46 +1964,6 @@
|
|||||||
_converse.roster.addAndSubscribe(jid, _.isEmpty(name)? jid: name);
|
_converse.roster.addAndSubscribe(jid, _.isEmpty(name)? jid: name);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
'chats': {
|
|
||||||
'open' (jids, attrs) {
|
|
||||||
if (_.isUndefined(jids)) {
|
|
||||||
_converse.log("chats.open: You need to provide at least one JID", Strophe.LogLevel.ERROR);
|
|
||||||
return null;
|
|
||||||
} else if (_.isString(jids)) {
|
|
||||||
return _converse.getViewForChatBox(
|
|
||||||
_converse.chatboxes.getChatBox(jids, true, attrs).trigger('show')
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return _.map(jids, (jid) =>
|
|
||||||
_converse.getViewForChatBox(
|
|
||||||
_converse.chatboxes.getChatBox(jid, true, attrs).trigger('show')
|
|
||||||
)
|
|
||||||
);
|
|
||||||
},
|
|
||||||
'get' (jids) {
|
|
||||||
if (_.isUndefined(jids)) {
|
|
||||||
const result = [];
|
|
||||||
_converse.chatboxes.each(function (chatbox) {
|
|
||||||
// FIXME: Leaky abstraction from MUC. We need to add a
|
|
||||||
// base type for chat boxes, and check for that.
|
|
||||||
if (chatbox.get('type') !== 'chatroom') {
|
|
||||||
result.push(_converse.getViewForChatBox(chatbox));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return result;
|
|
||||||
} else if (_.isString(jids)) {
|
|
||||||
return _converse.getViewForChatBox(_converse.chatboxes.getChatBox(jids));
|
|
||||||
}
|
|
||||||
return _.map(jids,
|
|
||||||
_.partial(
|
|
||||||
_.flow(
|
|
||||||
_converse.chatboxes.getChatBox.bind(_converse.chatboxes),
|
|
||||||
_converse.getViewForChatBox.bind(_converse)
|
|
||||||
), _, true
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
'tokens': {
|
'tokens': {
|
||||||
'get' (id) {
|
'get' (id) {
|
||||||
if (!_converse.expose_rid_and_sid || _.isUndefined(_converse.connection)) {
|
if (!_converse.expose_rid_and_sid || _.isUndefined(_converse.connection)) {
|
||||||
|
@ -13,8 +13,8 @@
|
|||||||
"tpl!toggle_chats",
|
"tpl!toggle_chats",
|
||||||
"tpl!trimmed_chat",
|
"tpl!trimmed_chat",
|
||||||
"tpl!chats_panel",
|
"tpl!chats_panel",
|
||||||
"converse-controlbox",
|
|
||||||
"converse-chatview",
|
"converse-chatview",
|
||||||
|
"converse-controlbox",
|
||||||
"converse-muc"
|
"converse-muc"
|
||||||
], factory);
|
], factory);
|
||||||
}(this, function (
|
}(this, function (
|
||||||
@ -27,7 +27,7 @@
|
|||||||
) {
|
) {
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
const { _ , utils, Backbone, b64_sha1, moment } = converse.env;
|
const { _ , utils, Backbone, Promise, Strophe, b64_sha1, moment } = converse.env;
|
||||||
|
|
||||||
converse.plugins.add('converse-minimize', {
|
converse.plugins.add('converse-minimize', {
|
||||||
overrides: {
|
overrides: {
|
||||||
@ -37,15 +37,6 @@
|
|||||||
//
|
//
|
||||||
// New functions which don't exist yet can also be added.
|
// New functions which don't exist yet can also be added.
|
||||||
|
|
||||||
initChatBoxes () {
|
|
||||||
const { _converse } = this.__super__;
|
|
||||||
const result = this.__super__.initChatBoxes.apply(this, arguments);
|
|
||||||
_converse.minimized_chats = new _converse.MinimizedChats({
|
|
||||||
model: _converse.chatboxes
|
|
||||||
});
|
|
||||||
return result;
|
|
||||||
},
|
|
||||||
|
|
||||||
registerGlobalEventHandlers () {
|
registerGlobalEventHandlers () {
|
||||||
const { _converse } = this.__super__;
|
const { _converse } = this.__super__;
|
||||||
$(window).on("resize", _.debounce(function (ev) {
|
$(window).on("resize", _.debounce(function (ev) {
|
||||||
@ -251,29 +242,31 @@
|
|||||||
// fullscreen. In this case we don't trim.
|
// fullscreen. In this case we don't trim.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const $minimized = _converse.minimized_chats.$el,
|
_converse.api.waitUntil('chatBoxesInitialized').then(() => {
|
||||||
minimized_width = _.includes(this.model.pluck('minimized'), true) ? $minimized.outerWidth(true) : 0,
|
const $minimized = _.get(_converse.minimized_chats, '$el'),
|
||||||
new_id = newchat ? newchat.model.get('id') : null;
|
minimized_width = _.includes(this.model.pluck('minimized'), true) ? $minimized.outerWidth(true) : 0,
|
||||||
|
new_id = newchat ? newchat.model.get('id') : null;
|
||||||
|
|
||||||
const boxes_width = _.reduce(
|
const boxes_width = _.reduce(
|
||||||
this.xget(new_id),
|
this.xget(new_id),
|
||||||
(memo, view) => memo + this.getChatBoxWidth(view),
|
(memo, view) => memo + this.getChatBoxWidth(view),
|
||||||
newchat ? newchat.$el.outerWidth(true) : 0);
|
newchat ? newchat.$el.outerWidth(true) : 0);
|
||||||
|
|
||||||
if ((minimized_width + boxes_width) > $('body').outerWidth(true)) {
|
if ((minimized_width + boxes_width) > $('body').outerWidth(true)) {
|
||||||
const oldest_chat = this.getOldestMaximizedChat([new_id]);
|
const oldest_chat = this.getOldestMaximizedChat([new_id]);
|
||||||
if (oldest_chat) {
|
if (oldest_chat) {
|
||||||
// We hide the chat immediately, because waiting
|
// We hide the chat immediately, because waiting
|
||||||
// for the event to fire (and letting the
|
// for the event to fire (and letting the
|
||||||
// ChatBoxView hide it then) causes race
|
// ChatBoxView hide it then) causes race
|
||||||
// conditions.
|
// conditions.
|
||||||
const view = this.get(oldest_chat.get('id'));
|
const view = this.get(oldest_chat.get('id'));
|
||||||
if (view) {
|
if (view) {
|
||||||
view.hide();
|
view.hide();
|
||||||
|
}
|
||||||
|
oldest_chat.minimize();
|
||||||
}
|
}
|
||||||
oldest_chat.minimize();
|
|
||||||
}
|
}
|
||||||
}
|
}).catch(_.partial(_converse.log, _, Strophe.LogLevel.FATAL));
|
||||||
},
|
},
|
||||||
|
|
||||||
getOldestMaximizedChat (exclude_ids) {
|
getOldestMaximizedChat (exclude_ids) {
|
||||||
@ -312,6 +305,8 @@
|
|||||||
no_trimming: false, // Set to true for phantomjs tests (where browser apparently has no width)
|
no_trimming: false, // Set to true for phantomjs tests (where browser apparently has no width)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
_converse.api.promises.add('minimizedChatsInitialized');
|
||||||
|
|
||||||
_converse.MinimizedChatBoxView = Backbone.View.extend({
|
_converse.MinimizedChatBoxView = Backbone.View.extend({
|
||||||
tagName: 'div',
|
tagName: 'div',
|
||||||
className: 'chat-head',
|
className: 'chat-head',
|
||||||
@ -492,7 +487,17 @@
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const renderMinimizeButton = function (view) {
|
Promise.all([
|
||||||
|
_converse.api.waitUntil('connectionInitialized'),
|
||||||
|
_converse.api.waitUntil('chatBoxesInitialized')
|
||||||
|
]).then(() => {
|
||||||
|
_converse.minimized_chats = new _converse.MinimizedChats({
|
||||||
|
model: _converse.chatboxes
|
||||||
|
});
|
||||||
|
_converse.emit('minimizedChatsInitialized');
|
||||||
|
}).catch(_.partial(_converse.log, _, Strophe.LogLevel.FATAL));
|
||||||
|
|
||||||
|
_converse.on('chatBoxOpened', function renderMinimizeButton (view) {
|
||||||
// Inserts a "minimize" button in the chatview's header
|
// Inserts a "minimize" button in the chatview's header
|
||||||
const $el = view.$el.find('.toggle-chatbox-button');
|
const $el = view.$el.find('.toggle-chatbox-button');
|
||||||
const $new_el = tpl_chatbox_minimize(
|
const $new_el = tpl_chatbox_minimize(
|
||||||
@ -503,8 +508,7 @@
|
|||||||
} else {
|
} else {
|
||||||
view.$el.find('.close-chatbox-button').after($new_el);
|
view.$el.find('.close-chatbox-button').after($new_el);
|
||||||
}
|
}
|
||||||
};
|
});
|
||||||
_converse.on('chatBoxOpened', renderMinimizeButton);
|
|
||||||
|
|
||||||
_converse.on('controlBoxOpened', function (chatbox) {
|
_converse.on('controlBoxOpened', function (chatbox) {
|
||||||
// Wrapped in anon method because at scan time, chatboxviews
|
// Wrapped in anon method because at scan time, chatboxviews
|
||||||
|
@ -14,7 +14,8 @@
|
|||||||
"tpl!requesting_contact",
|
"tpl!requesting_contact",
|
||||||
"tpl!roster",
|
"tpl!roster",
|
||||||
"tpl!roster_filter",
|
"tpl!roster_filter",
|
||||||
"tpl!roster_item"
|
"tpl!roster_item",
|
||||||
|
"converse-chatboxes"
|
||||||
], factory);
|
], factory);
|
||||||
}(this, function (
|
}(this, function (
|
||||||
$,
|
$,
|
||||||
@ -75,6 +76,7 @@
|
|||||||
allow_contact_removal: true,
|
allow_contact_removal: true,
|
||||||
show_toolbar: true,
|
show_toolbar: true,
|
||||||
});
|
});
|
||||||
|
_converse.api.promises.add('rosterViewInitialized');
|
||||||
|
|
||||||
const STATUSES = {
|
const STATUSES = {
|
||||||
'dnd': __('This contact is busy'),
|
'dnd': __('This contact is busy'),
|
||||||
@ -949,6 +951,7 @@
|
|||||||
'model': _converse.rostergroups
|
'model': _converse.rostergroups
|
||||||
});
|
});
|
||||||
_converse.rosterview.render();
|
_converse.rosterview.render();
|
||||||
|
_converse.emit('rosterViewInitialized');
|
||||||
};
|
};
|
||||||
_converse.api.listen.on('rosterInitialized', initRoster);
|
_converse.api.listen.on('rosterInitialized', initRoster);
|
||||||
_converse.api.listen.on('rosterReadyAfterReconnection', initRoster);
|
_converse.api.listen.on('rosterReadyAfterReconnection', initRoster);
|
||||||
|
@ -131,28 +131,32 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
const updateVCardForChatBox = function (chatbox) {
|
const updateVCardForChatBox = function (chatbox) {
|
||||||
if (!_converse.use_vcards) { return; }
|
if (!_converse.use_vcards || chatbox.model.get('type') === 'headline') {
|
||||||
const jid = chatbox.model.get('jid'),
|
return;
|
||||||
contact = _converse.roster.get(jid);
|
|
||||||
if ((contact) && (!contact.get('vcard_updated'))) {
|
|
||||||
_converse.getVCard(
|
|
||||||
jid,
|
|
||||||
function (iq, jid, fullname, image, image_type, url) {
|
|
||||||
chatbox.model.save({
|
|
||||||
'fullname' : fullname || jid,
|
|
||||||
'url': url,
|
|
||||||
'image_type': image_type,
|
|
||||||
'image': image
|
|
||||||
});
|
|
||||||
},
|
|
||||||
function () {
|
|
||||||
_converse.log(
|
|
||||||
"updateVCardForChatBox: Error occured while fetching vcard",
|
|
||||||
Strophe.LogLevel.ERROR
|
|
||||||
);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
_converse.api.waitUntil('rosterInitialized').then(() => {
|
||||||
|
const jid = chatbox.model.get('jid'),
|
||||||
|
contact = _converse.roster.get(jid);
|
||||||
|
if ((contact) && (!contact.get('vcard_updated'))) {
|
||||||
|
_converse.getVCard(
|
||||||
|
jid,
|
||||||
|
function (iq, jid, fullname, image, image_type, url) {
|
||||||
|
chatbox.model.save({
|
||||||
|
'fullname' : fullname || jid,
|
||||||
|
'url': url,
|
||||||
|
'image_type': image_type,
|
||||||
|
'image': image
|
||||||
|
});
|
||||||
|
},
|
||||||
|
function () {
|
||||||
|
_converse.log(
|
||||||
|
"updateVCardForChatBox: Error occured while fetching vcard",
|
||||||
|
Strophe.LogLevel.ERROR
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}).catch(_.partial(_converse.log, _, Strophe.LogLevel.FATAL));
|
||||||
};
|
};
|
||||||
_converse.on('chatBoxInitialized', updateVCardForChatBox);
|
_converse.on('chatBoxInitialized', updateVCardForChatBox);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user