Commit 4c3645c5 authored by JC Brand's avatar JC Brand

Merge branch 'master' into converse-omemo

parents e774e9d1 0a00c617
......@@ -30,6 +30,7 @@
- Documentation includes utf-8 charset to make minfied versions compatible across platforms. #1017
- #1026 Typing in MUC shows "Typing from another device"
- #1039 Multi-option data form elements not shown and saved correctly
- #1143 Able to send blank message
### API changes
......@@ -39,6 +40,9 @@
- New API method `_converse.api.vcard.update`.
- The `contactStatusChanged` event has been renamed to `contactPresenceChanged`
and a event `presenceChanged` is now also triggered on the contact.
- `_converse.api.chats.open` and `_converse.api.rooms.open` now returns a
`Presence` which resolves with the `Backbone.Model` representing the chat
object.
## UI changes
......
......@@ -227,6 +227,7 @@ check: eslint
html:
rm -rf $(BUILDDIR)/html
$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
make apidoc
@echo
@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
......
This diff is collapsed.
# -*- coding: utf-8 -*-
#
# Converse.js documentation build configuration file, created by
# Converse documentation build configuration file, created by
# sphinx-quickstart on Fri Apr 26 20:48:03 2013.
#
# This file is execfile()d with the current directory set to its containing dir.
......@@ -40,8 +40,8 @@ source_suffix = '.rst'
master_doc = 'index'
# General information about the project.
project = u'Converse.js'
copyright = u'2017, JC Brand'
project = u'Converse'
copyright = u'2018, JC Brand'
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
......@@ -108,7 +108,7 @@ html_logo = "_static/conversejs_small.png"
# theme further.
html_theme_options = {
# Navigation bar title. (Default: ``project`` value)
'navbar_title': "Converse.js",
'navbar_title': "Converse",
# Tab name for entire site. (Default: "Site")
'navbar_site_name': "Table of Contents",
# A list of tuples containing pages or urls to link to.
......@@ -229,7 +229,7 @@ latex_elements = {
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title, author, documentclass [howto/manual]).
latex_documents = [
('index', 'Conversejs.tex', u'Converse.js Documentation',
('index', 'Conversejs.tex', u'Converse Documentation',
u'JC Brand', 'manual'),
]
......@@ -259,7 +259,7 @@ latex_documents = [
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
('index', 'conversejs', u'Converse.js Documentation',
('index', 'conversejs', u'Converse Documentation',
[u'JC Brand'], 1)
]
......@@ -273,8 +273,8 @@ man_pages = [
# (source start file, target name, title, author,
# dir menu entry, description, category)
texinfo_documents = [
('index', 'Conversejs', u'Converse.js Documentation',
u'JC Brand', 'Conversejs', 'Open Source XMPP webchat',
('index', 'Conversejs', u'Converse Documentation',
u'JC Brand', 'Converse', 'Open Source XMPP webchat',
'Miscellaneous'),
]
......
This diff is collapsed.
......@@ -8,13 +8,13 @@
Development
===========
Welcome to the developer documentation of converse.js. Read the documentation
Welcome to the developer documentation of Converse. Read the documentation
linked to below, if you want to add new features or create your own customized
version of converse.js.
version of Converse.
Converse.js itself composed of plugins, and exposes an API with which you can
Converse itself composed of plugins, and exposes an API with which you can
create and register your own plugins. This is the recommended way to customize
or add new functionality to converse.js.
or add new functionality to Converse.
.. toctree::
:maxdepth: 2
......@@ -22,6 +22,7 @@ or add new functionality to converse.js.
developer_guidelines
style_guide
plugin_development
api/index
developer_api
events
other_frameworks
......
......@@ -8,19 +8,19 @@
Setup and integration
=====================
This page documents what you'll need to do to be able to connect Converse.js with
This page documents what you'll need to do to be able to connect Converse with
your own XMPP server and to better integrate it into your website.
At the very least you'll need Converse.js and an :ref:`XMPP server` with
At the very least you'll need Converse and an :ref:`XMPP server` with
:ref:`websocket-section` or :ref:`BOSH-section` enabled. That's definitely
enough to simply demo Converse.js or to do development work on it.
enough to simply demo Converse or to do development work on it.
However, if you want to more fully integrate it into a website or intranet,
However, if you want to more fully integrate it into a website
then you'll likely need to set up more services and components.
The diagram below shows a fairly common setup for a website or intranet:
* Converse.js runs in the web-browser on the user's device.
* Converse runs in the web-browser on the user's device.
* It communicates with the XMPP server via BOSH or websocket which is usually
reverse-proxied by a web-server in order to overcome cross-site scripting
......@@ -34,13 +34,12 @@ The diagram below shows a fairly common setup for a website or intranet:
the XMPP server is configured to use, so that users can log in with those
credentials.
* Usually (but optionally) there is a backend web application which hosts a
website in which Converse.js appears.
website in which Converse appears.
.. figure:: images/diagram.png
:align: center
:alt: A diagram of a possible setup, consisting of Converse.js, a web server, a backend web application, an XMPP server, a user directory such as LDAP and an XMPP server.
:alt: A diagram of a possible setup, consisting of Converse, a web server, a backend web application, an XMPP server, a user directory such as LDAP and an XMPP server.
This diagram shows the various services in a fairly common setup (image generated with `draw.io <https://draw.io>`_).
......@@ -53,11 +52,11 @@ The various components
An XMPP server
==============
*Converse.js* implements `XMPP <http://xmpp.org/about-xmpp/>`_ as its
*Converse* uses `XMPP <http://xmpp.org/about-xmpp/>`_ as its
messaging protocol, and therefore needs to connect to an XMPP/Jabber
server (Jabber® is an older and more user-friendly synonym for XMPP).
You can connect to public XMPP servers like ``jabber.org`` but if you want to
You can connect to public XMPP servers like ``conversejs.org`` but if you want to
have :ref:`session support <session-support>` you'll have to set up your own XMPP server.
You can find a list of public XMPP servers/providers on `xmpp.net <https://list.jabber.at>`_
......@@ -79,7 +78,7 @@ stanzas to be sent over an HTTP connection.
HTTP connections are stateless and usually shortlived.
XMPP connections on the other hand are stateful and usually last much longer.
So to enable a web application like *Converse.js* to communicate with an XMPP
So to enable a web application like *Converse* to communicate with an XMPP
server, we need a proxy which acts as a bridge between these two protocols.
This is the job of a BOSH connection manager. BOSH (Bidirectional-streams Over
......@@ -87,7 +86,7 @@ Synchronous HTTP) is a protocol for allowing XMPP communication over HTTP. The
protocol is defined in `XEP-0206: XMPP Over BOSH <http://xmpp.org/extensions/xep-0206.html>`_.
Popular XMPP servers such as `Ejabberd <http://www.ejabberd.im>`_,
prosody `(mod_bosh) <http://prosody.im/doc/setting_up_bosh>`_ and
Prosody `(mod_bosh) <http://prosody.im/doc/setting_up_bosh>`_ and
`OpenFire <http://www.igniterealtime.org/projects/openfire/>`_ all include
their own BOSH connection managers (but you usually have to enable them in the
configuration).
......@@ -98,14 +97,15 @@ https://conversejs.org does), then you'll need a standalone connection manager.
For a standalone manager, see for example `Punjab <https://github.com/twonds/punjab>`_
and `node-xmpp-bosh <https://github.com/dhruvbird/node-xmpp-bosh>`_.
The demo on the `Converse.js homepage <http://conversejs.org>`_ uses a connection
The demo on the `Converse homepage <http://conversejs.org>`_ uses a connection
manager located at https://conversejs.org/http-bind.
This connection manager is available for testing purposes only, please don't
use it in production.
Refer to the :ref:`bosh-service-url` configuration setting for information on
how to configure Converse.js to connect to a BOSH URL.
how to configure Converse to connect to a BOSH URL.
.. _`websocket-section`:
......@@ -122,7 +122,7 @@ HTTP. Therefore BOSH, which operates over HTTP, doesn't apply to websockets.
does the node-xmpp-bosh connection manager.
Refer to the :ref:`websocket-url` configuration setting for information on how to
configure Converse.js to connect to a websocket URL.
configure Converse to connect to a websocket URL.
The Webserver
=============
......@@ -133,7 +133,7 @@ Overcoming cross-domain request restrictions
Lets say your domain is *example.org*, but the domain of your connection
manager is *example.com*.
HTTP requests are made by *Converse.js* to the BOSH connection manager via
HTTP requests are made by *Converse* to the BOSH connection manager via
XmlHttpRequests (XHR). Until recently, it was not possible to make such
requests to a different domain than the one currently being served
(to prevent XSS attacks).
......@@ -159,7 +159,7 @@ Examples:
Assuming your site is accessible on port ``80`` for the domain ``mysite.com``
and your connection manager manager is running at ``someothersite.com/http-bind``.
The *bosh_service_url* value you want to give Converse.js to overcome
The *bosh_service_url* value you want to give Converse to overcome
the cross-domain restriction is ``mysite.com/http-bind`` and not
``someothersite.com/http-bind``.
......@@ -192,6 +192,32 @@ Apache
</VirtualHost>
.. note::
If you're getting XML parsing errors for your BOSH endpoint, for
example::
XML Parsing Error: mismatched tag. Expected: </hr>.
Location: https://example.org/http-bind/
Line Number 6, Column 3: bosh-anon:6:3
Also ERROR: request id 12.2 error 504 happened
Then your BOSH proxy is returning an HTML error page (for a 504 error in
the above example).
This might be because your webserver and BOSH proxy have the same timeout
for BOSH requests. Because the webserver receives the request slightly earlier,
it gives up a few microseconds before the XMPP server’s empty result and thus returns a
504 error page containing HTML to browser, which then gets parsed as if its
XML.
To fix this, make sure that the webserver's timeout is slightly higher.
In Nginx you can do this by adding ``proxy_read_timeout 61;``;
From Converse 4.0.0 onwards the default ``wait`` time is set to 59 seconds, to avoid
this problem.
.. _`session-support`:
Single Session Support
......@@ -215,7 +241,7 @@ authenticated BOSH session with the XMPP server or a standalone `BOSH <http://xm
connection manager.
Once authenticated, it receives RID and SID tokens which need to be passed
on to converse.js upon pa. Converse.js will then attach to that same session using
on to converse.js upon pa. Converse will then attach to that same session using
those tokens.
It's called "prebind" because you bind to the BOSH session beforehand, and then
......@@ -250,7 +276,7 @@ converse.js can then attach to.
A BOSH server acts as a bridge between HTTP, the protocol of the web, and
XMPP, the instant messaging protocol.
Converse.js can only communicate via HTTP (or websocket, in which case BOSH can't be used).
Converse can only communicate via HTTP (or websocket, in which case BOSH can't be used).
It cannot open TCP sockets to communicate to an XMPP server directly.
So the BOSH server acts as a middle man, translating our HTTP requests into XMPP stanzas and vice versa.
......@@ -303,7 +329,7 @@ authentication to external services. They are listed in the `Prosody community m
page <https://modules.prosody.im/>`_. Other XMPP servers have similar plugin modules.
If your web-application has access to the same credentials, it can send those
credentials to Converse.js so that user's are automatically logged in when the
credentials to Converse so that user's are automatically logged in when the
page loads.
This is can be done by setting :ref:`auto_login` to true and configuring the
......@@ -316,7 +342,7 @@ The first option has the drawback that your web-application needs to know the
XMPP credentials of your users and that they need to be stored in the clear.
The second option has that same drawback and it also needs to pass those
credentials to Converse.js.
credentials to Converse.
To avoid these drawbacks, you can instead let your backend web application
generate temporary authentication tokens which are then sent to the XMPP server
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
......@@ -2410,9 +2410,9 @@
"dev": true
},
"bootstrap.native": {
"version": "2.0.22",
"resolved": "https://registry.npmjs.org/bootstrap.native/-/bootstrap.native-2.0.22.tgz",
"integrity": "sha512-eypi4y1eKJoRt8cTwkZCI3QQ7W04rbv4VU1nBjBshqNXkONI7jO6tG3qZTwq9Zd+gDoeaQASyk6V185y+Y7KHQ==",
"version": "2.0.23",
"resolved": "https://registry.npmjs.org/bootstrap.native/-/bootstrap.native-2.0.23.tgz",
"integrity": "sha512-bcbVgqIjRkyiHd6DN8Y18BYjJTKvvnN3Msb7Yh6K5vGGsjRT3gV0IFKR3rcEYgJ5Kvg1egL9exIfN/Hvwn4wNA==",
"dev": true
},
"bourbon": {
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
......@@ -66,47 +66,50 @@
it("shows the number of unread mentions received",
mock.initConverseWithPromises(
null, ['rosterGroupsFetched'], {},
null, ['rosterGroupsFetched', 'chatBoxesFetched'], {},
function (done, _converse) {
test_utils.createContacts(_converse, 'all').openControlBox();
_converse.emit('rosterContactsFetched');
var contacts_panel = _converse.chatboxviews.get('controlbox').contactspanel;
const sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
var sender_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
test_utils.openChatBoxFor(_converse, sender_jid);
var chatview = _converse.chatboxviews.get(sender_jid);
chatview.model.set({'minimized': true});
expect(_.isNull(_converse.chatboxviews.el.querySelector('.restore-chat .message-count'))).toBeTruthy();
expect(_.isNull(_converse.rosterview.el.querySelector('.msgs-indicator'))).toBeTruthy();
var msg = $msg({
from: sender_jid,
to: _converse.connection.jid,
type: 'chat',
id: (new Date()).getTime()
}).c('body').t('hello').up()
.c('active', {'xmlns': 'http://jabber.org/protocol/chatstates'}).tree();
_converse.chatboxes.onMessage(msg);
expect(_converse.chatboxviews.el.querySelector('.restore-chat .message-count').textContent).toBe('1');
expect(_converse.rosterview.el.querySelector('.msgs-indicator').textContent).toBe('1');
msg = $msg({
from: sender_jid,
to: _converse.connection.jid,
type: 'chat',
id: (new Date()).getTime()
}).c('body').t('hello again').up()
.c('active', {'xmlns': 'http://jabber.org/protocol/chatstates'}).tree();
_converse.chatboxes.onMessage(msg);
expect(_converse.chatboxviews.el.querySelector('.restore-chat .message-count').textContent).toBe('2');
expect(_converse.rosterview.el.querySelector('.msgs-indicator').textContent).toBe('2');
chatview.model.set({'minimized': false});
expect(_.isNull(_converse.chatboxviews.el.querySelector('.restore-chat .message-count'))).toBeTruthy();
expect(_.isNull(_converse.rosterview.el.querySelector('.msgs-indicator'))).toBeTruthy();
done();
return test_utils.waitUntil(() => _converse.chatboxes.length).then(() => {
const chatview = _converse.chatboxviews.get(sender_jid);
chatview.model.set({'minimized': true});
expect(_.isNull(_converse.chatboxviews.el.querySelector('.restore-chat .message-count'))).toBeTruthy();
expect(_.isNull(_converse.rosterview.el.querySelector('.msgs-indicator'))).toBeTruthy();
var msg = $msg({
from: sender_jid,
to: _converse.connection.jid,
type: 'chat',
id: (new Date()).getTime()
}).c('body').t('hello').up()
.c('active', {'xmlns': 'http://jabber.org/protocol/chatstates'}).tree();
_converse.chatboxes.onMessage(msg);
expect(_converse.chatboxviews.el.querySelector('.restore-chat .message-count').textContent).toBe('1');
expect(_converse.rosterview.el.querySelector('.msgs-indicator').textContent).toBe('1');
msg = $msg({
from: sender_jid,
to: _converse.connection.jid,
type: 'chat',
id: (new Date()).getTime()
}).c('body').t('hello again').up()
.c('active', {'xmlns': 'http://jabber.org/protocol/chatstates'}).tree();
_converse.chatboxes.onMessage(msg);
expect(_converse.chatboxviews.el.querySelector('.restore-chat .message-count').textContent).toBe('2');
expect(_converse.rosterview.el.querySelector('.msgs-indicator').textContent).toBe('2');
chatview.model.set({'minimized': false});
expect(_.isNull(_converse.chatboxviews.el.querySelector('.restore-chat .message-count'))).toBeTruthy();
expect(_.isNull(_converse.rosterview.el.querySelector('.msgs-indicator'))).toBeTruthy();
done();
});
}));
});
......
(function (root, factory) {
define([
"jquery",
"jasmine",
"mock",
"test-utils"], factory);
} (this, function ($, jasmine, mock, test_utils) {
var b64_sha1 = converse.env.b64_sha1;
var _ = converse.env._;
} (this, function (jasmine, mock, test_utils) {
const b64_sha1 = converse.env.b64_sha1,
_ = converse.env._,
u = converse.env.utils;
describe("Converse", function() {
......@@ -274,59 +274,72 @@
describe("The \"chats\" API", function() {
it("has a method 'get' which returns the chatbox model", mock.initConverseWithPromises(
null, ['rosterInitialized'], {}, function (done, _converse) {
it("has a method 'get' which returns the promise that resolves to a chat model", mock.initConverseWithPromises(
null, ['rosterInitialized', 'chatBoxesInitialized'], {}, function (done, _converse) {
test_utils.openControlBox();
test_utils.createContacts(_converse, 'current');
test_utils.createContacts(_converse, 'current', 2);
_converse.emit('rosterContactsFetched');
// Test on chat that doesn't exist.
expect(_converse.api.chats.get('non-existing@jabber.org')).toBeFalsy();
var jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
const jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
const jid2 = mock.cur_names[1].replace(/ /g,'.').toLowerCase() + '@localhost';
// Test on chat that's not open
var box = _converse.api.chats.get(jid);
let box = _converse.api.chats.get(jid);
expect(typeof box === 'undefined').toBeTruthy();
var chatboxview = _converse.chatboxviews.get(jid);
// Test for single JID
expect(_converse.chatboxes.length).toBe(1);
// Test for one JID
test_utils.openChatBoxFor(_converse, jid);
box = _converse.api.chats.get(jid);
test_utils.waitUntil(() => _converse.chatboxes.length == 1).then(() => {
box = _converse.api.chats.get(jid);
expect(box instanceof Object).toBeTruthy();
expect(box.get('box_id')).toBe(b64_sha1(jid));
const chatboxview = _converse.chatboxviews.get(jid);
expect(u.isVisible(chatboxview.el)).toBeTruthy();
// Test for multiple JIDs
test_utils.openChatBoxFor(_converse, jid2);
return test_utils.waitUntil(() => _converse.chatboxes.length == 2);
}).then(() => {
const list = _converse.api.chats.get([jid, jid2]);
expect(_.isArray(list)).toBeTruthy();
expect(list[0].get('box_id')).toBe(b64_sha1(jid));
expect(list[1].get('box_id')).toBe(b64_sha1(jid2));
done();
}).catch(_.partial(console.error, _));
}));
it("has a method 'open' which opens and returns a promise that resolves to a chat model", mock.initConverseWithPromises(
null, ['rosterGroupsFetched', 'chatBoxesInitialized'], {}, function (done, _converse) {
test_utils.openControlBox();
test_utils.createContacts(_converse, 'current', 2);
_converse.emit('rosterContactsFetched');
const jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
const jid2 = mock.cur_names[1].replace(/ /g,'.').toLowerCase() + '@localhost';
// Test on chat that doesn't exist.
expect(_converse.api.chats.get('non-existing@jabber.org')).toBeFalsy();
return _converse.api.chats.open(jid).then((box) => {
expect(box instanceof Object).toBeTruthy();
expect(box.get('box_id')).toBe(b64_sha1(jid));
chatboxview = _converse.chatboxviews.get(jid);
expect($(chatboxview.el).is(':visible')).toBeTruthy();
expect(
_.keys(box),
['close', 'endOTR', 'focus', 'get', 'initiateOTR', 'is_chatroom', 'maximize', 'minimize', 'open', 'set']
);
const chatboxview = _converse.chatboxviews.get(jid);
expect(u.isVisible(chatboxview.el)).toBeTruthy();
// Test for multiple JIDs
var jid2 = mock.cur_names[1].replace(/ /g,'.').toLowerCase() + '@localhost';
test_utils.openChatBoxFor(_converse, jid2);
var list = _converse.api.chats.get([jid, jid2]);
return _converse.api.chats.open([jid, jid2]);
}).then((list) => {
expect(_.isArray(list)).toBeTruthy();
expect(list[0].get('box_id')).toBe(b64_sha1(jid));
expect(list[1].get('box_id')).toBe(b64_sha1(jid2));
done();
}));
it("has a method 'open' which opens and returns the chatbox model", mock.initConverseWithPromises(
null, ['rosterGroupsFetched'], {}, function (done, _converse) {
test_utils.openControlBox();
test_utils.createContacts(_converse, 'current');
var jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
var chatboxview;
// Test on chat that doesn't exist.
expect(_converse.api.chats.get('non-existing@jabber.org')).toBeFalsy();
var box = _converse.api.chats.open(jid);
expect(box instanceof Object).toBeTruthy();
expect(box.get('box_id')).toBe(b64_sha1(jid));
expect(
_.keys(box),
['close', 'focus', 'get', 'is_chatroom', 'maximize', 'minimize', 'open', 'set']
);
chatboxview = _converse.chatboxviews.get(jid);
expect($(chatboxview.el).is(':visible')).toBeTruthy();
// Test for multiple JIDs
var jid2 = mock.cur_names[1].replace(/ /g,'.').toLowerCase() + '@localhost';
var list = _converse.api.chats.open([jid, jid2]);
expect(_.isArray(list)).toBeTruthy();
expect(list[0].get('box_id')).toBe(b64_sha1(jid));
expect(list[1].get('box_id')).toBe(b64_sha1(jid2));
done();
});
}));
});
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
......@@ -92,12 +92,14 @@
it("can be sent without a hint",
mock.initConverseWithPromises(
null, ['rosterGroupsFetched'], {},
null, ['rosterGroupsFetched', 'chatBoxesFetched'], {},
function (done, _converse) {
test_utils.createContacts(_converse, 'current');
test_utils.createContacts(_converse, 'current', 1);
_converse.emit('rosterContactsFetched');
test_utils.openControlBox();
var contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
const contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
// XXX: We need to send a presence from the contact, so that we
// have a resource, that resource is then queried to see
......@@ -108,9 +110,9 @@
'to': 'dummy@localhost'
});
_converse.connection._dataRecv(test_utils.createRequest(presence));
test_utils.openChatBoxFor(_converse, contact_jid);
test_utils.waitUntilDiscoConfirmed(_converse, contact_jid+'/phone', [], [Strophe.NS.SPOILER]).then(function () {
test_utils.openChatBoxFor(_converse, contact_jid)
.then(() => test_utils.waitUntilDiscoConfirmed(_converse, contact_jid+'/phone', [], [Strophe.NS.SPOILER]))
.then(() => {
var view = _converse.chatboxviews.get(contact_jid);
spyOn(view, 'onMessageSubmitted').and.callThrough();
spyOn(_converse.connection, 'send');
......@@ -167,10 +169,12 @@
it("can be sent with a hint",
mock.initConverseWithPromises(
null, ['rosterGroupsFetched'], {},
null, ['rosterGroupsFetched', 'chatBoxesFetched'], {},
function (done, _converse) {
test_utils.createContacts(_converse, 'current');
test_utils.createContacts(_converse, 'current', 1);
_converse.emit('rosterContactsFetched');
test_utils.openControlBox();
var contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
......@@ -183,9 +187,9 @@
'to': 'dummy@localhost'
});
_converse.connection._dataRecv(test_utils.createRequest(presence));
test_utils.openChatBoxFor(_converse, contact_jid);
test_utils.waitUntilDiscoConfirmed(_converse, contact_jid+'/phone', [], [Strophe.NS.SPOILER]).then(function () {
test_utils.openChatBoxFor(_converse, contact_jid)
.then(() => test_utils.waitUntilDiscoConfirmed(_converse, contact_jid+'/phone', [], [Strophe.NS.SPOILER]))
.then(() => {
var view = _converse.chatboxviews.get(contact_jid);
var spoiler_toggle = view.el.querySelector('.toggle-compose-spoiler');
spoiler_toggle.click();
......@@ -206,17 +210,17 @@
expect(view.onMessageSubmitted).toHaveBeenCalled();
/* Test the XML stanza
*
* <message from="dummy@localhost/resource"
* to="max.frankfurter@localhost"
* type="chat"
* id="4547c38b-d98b-45a5-8f44-b4004dbc335e"
* xmlns="jabber:client">
* <body>This is the spoiler</body>
* <active xmlns="http://jabber.org/protocol/chatstates"/>
* <spoiler xmlns="urn:xmpp:spoiler:0">This is the hint</spoiler>
* </message>"
*/
*
* <message from="dummy@localhost/resource"
* to="max.frankfurter@localhost"
* type="chat"
* id="4547c38b-d98b-45a5-8f44-b4004dbc335e"
* xmlns="jabber:client">
* <body>This is the spoiler</body>
* <active xmlns="http://jabber.org/protocol/chatstates"/>
* <spoiler xmlns="urn:xmpp:spoiler:0">This is the hint</spoiler>
* </message>"
*/
var stanza = _converse.connection.send.calls.argsFor(0)[0].tree();
var spoiler_el = stanza.querySelector('spoiler[xmlns="urn:xmpp:spoiler:0"]');
......
This diff is collapsed.
This diff is collapsed.
......@@ -888,6 +888,9 @@
const textarea = this.el.querySelector('.chat-textarea'),
message = textarea.value;
if (!message.replace(/\s/g, '').length) {
return;
}
let spoiler_hint;
if (this.model.get('composing_spoiler')) {
const hint_el = this.el.querySelector('form.sendXMPPMessage input.spoiler-hint');
......@@ -901,10 +904,8 @@
event.initEvent('input', true, true);
textarea.dispatchEvent(event);
if (message !== '') {
this.onMessageSubmitted(message, spoiler_hint);
_converse.emit('messageSend', message);
}
this.onMessageSubmitted(message, spoiler_hint);
_converse.emit('messageSend', message);
this.setChatState(_converse.ACTIVE);
},
......
This diff is collapsed.
......@@ -296,6 +296,7 @@
if (from !== null) {
iqresult.attrs({'to': from});
}
iqresult.c('query', attrs);
_.each(plugin._identities, (identity) => {
const attrs = {
'category': identity.category,
......
......@@ -30,11 +30,10 @@
if (!_.isArray(_converse.auto_join_rooms) && !_.isArray(_converse.auto_join_private_chats)) {
throw new Error("converse-embedded: auto_join_rooms must be an Array");
}
if (_converse.auto_join_rooms.length !== 1 && _converse.auto_join_private_chats.length !== 1) {
if (_converse.auto_join_rooms.length > 1 && _converse.auto_join_private_chats.length > 1) {
throw new Error("converse-embedded: It doesn't make "+
"sense to have the auto_join_rooms setting to zero or "+
"more then one, since only one chat room can be open "+
"at any time.");
"sense to have the auto_join_rooms setting more then one, "+
"since only one chat room can be open at any time.");
}
}
});
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment