Merge branch 'master' into converse-omemo

This commit is contained in:
JC Brand 2018-06-30 23:03:36 +02:00
commit 6785eff4a7
144 changed files with 122357 additions and 82048 deletions

View File

@ -1,9 +0,0 @@
{
"presets": [
["@babel/preset-env", {
"targets": {
"browsers": ["last 2 versions", "safari >= 10", "IE >= 11"]
}
}]
]
}

View File

@ -10,6 +10,7 @@
"extends": ["eslint:recommended", "plugin:lodash/canonical"],
"globals": {
"Promise": true,
"converse": true,
"define": true,
"require": true,
"sinon": true,

2
.gitignore vendored
View File

@ -13,6 +13,8 @@
.su?
builds/*
3rdparty/libsignal-protocol-javascript/
*.map
dist/converse-no-dependencies-es2015.js
analytics.js
inverse-analytics.js

View File

@ -7,7 +7,11 @@
- #161 XEP-0363: HTTP File Upload
- #194 Include entity capabilities in outgoing presence stanzas
- #337 API call to update a VCard
- #968 Use nickname from VCard when joining a room
- #1091 There's now only one CSS file for all view modes.
- #1094 Show room members who aren't currently online
- #1106 Support for Roster Versioning
- #1081 Allow for shift-enter to insert newlines
- It's now also possible to edit your VCard via the UI
- Automatically grow/shrink input as text is entered/removed
- MP4 and MP3 files when sent as XEP-0066 Out of Band Data, are now playable directly in chat
@ -16,6 +20,8 @@
- Add a checkbox to indicate whether a trusted device is being used or not.
If the device is not trusted, sessionStorage is used and all user data is deleted from the browser cache upon logout.
If the device is trusted, localStorage is used and user data is cached indefinitely.
- Initial support for XEP-0357 Push Notifications, specifically registering an "App Server".
- Add support for logging in via OAuth (see the [oauth_providers](https://conversejs.org/docs/html/configurations.html#oauth-providers) setting)
### Bugfixes

View File

@ -8,15 +8,16 @@ CHROMIUM ?= ./node_modules/.bin/run-headless-chromium
CLEANCSS ?= ./node_modules/clean-css-cli/bin/cleancss --skip-rebase
ESLINT ?= ./node_modules/.bin/eslint
HTTPSERVE ?= ./node_modules/.bin/http-server
HTTPSERVE_PORT ?= 8000
INKSCAPE ?= inkscape
HTTPSERVE_PORT ?= 8000
INKSCAPE ?= inkscape
JSDOC ?= ./node_modules/.bin/jsdoc
OXIPNG ?= oxipng
OXIPNG ?= oxipng
PAPER =
PO2JSON ?= ./node_modules/.bin/po2json
RJS ?= ./node_modules/.bin/r.js
RJS ?= ./node_modules/.bin/r.js
WEBPACK ?= ./node_modules/.bin/npx
SASS ?= ./.bundle/bin/sass
SED ?= sed
SED ?= sed
SPHINXBUILD ?= ./bin/sphinx-build
SPHINXOPTS =
UGLIFYJS ?= node_modules/.bin/uglifyjs
@ -73,7 +74,7 @@ serve_bg: dev
GETTEXT = xgettext --language="JavaScript" --keyword=__ --keyword=___ --from-code=UTF-8 --output=locale/converse.pot dist/converse-no-dependencies.js --package-name=Converse.js --copyright-holder="Jan-Carel Brand" --package-version=3.3.4 -c
.PHONY: pot
pot: dist/converse-no-dependencies.js
pot: dist/converse-no-dependencies-es2015.js
$(GETTEXT) 2>&1 > /dev/null; exit $$?;
.PHONY: po
@ -121,7 +122,7 @@ stamp-bundler: Gemfile
.PHONY: clean
clean:
rm -rf node_modules .bundle stamp-npm
rm -rf node_modules .bundle stamp-npm stamp-bundler
rm dist/*.min.js
rm css/website.min.css
rm css/converse.min.css
@ -134,10 +135,7 @@ dev: stamp-bundler stamp-npm
## Builds
.PHONY: css
css: dev sass/*.scss css/converse.css css/converse.min.css css/website.css css/website.min.css css/inverse.css css/inverse.min.css css/fonts.css
css/inverse.css:: dev sass sass
$(SASS) -I $(BOURBON) -I $(BOOTSTRAP) sass/inverse.scss css/inverse.css
css: dev sass/*.scss css/converse.css css/converse.min.css css/website.css css/website.min.css css/fonts.css
css/converse.css:: dev sass
$(SASS) -I $(BOURBON) -I $(BOOTSTRAP) sass/converse.scss css/converse.css
@ -158,12 +156,7 @@ watch: dev
.PHONY: watchjs
watchjs: dev
$(BABEL) --source-maps --watch=./src --out-dir=./builds
transpile: dev src
$(BABEL) --source-maps --out-dir=./builds ./src
$(BABEL) --source-maps --out-dir=./builds ./node_modules/backbone.vdomview/backbone.vdomview.js
touch transpile
./node_modules/.bin/npx webpack --mode=development --watch
.PHONY: logo
logo: logo/conversejs-transparent16.png \
@ -186,37 +179,34 @@ logo/conversejs-filled%.png:: logo/conversejs-filled.svg
$(OXIPNG) $@
BUILDS = dist/converse.js \
dist/converse.min.js \
dist/converse-headless.js \
dist/converse-headless.min.js \
dist/converse-no-dependencies.min.js \
dist/converse-no-dependencies.js
dist/converse.min.js \
dist/converse-headless.js \
dist/converse-headless.min.js \
dist/converse-no-dependencies.min.js \
dist/converse-no-dependencies.js \
dist/converse-no-dependencies-es2015.js
# dist/converse-esnext.js \
# dist/converse-esnext.min.js \
dist/converse.js: src webpack.config.js stamp-npm
./node_modules/.bin/npx webpack --mode=development
dist/converse.min.js: src webpack.config.js stamp-npm
./node_modules/.bin/npx webpack --mode=production
dist/converse-headless.js: src webpack.config.js stamp-npm
./node_modules/.bin/npx webpack --mode=development --type=headless
dist/converse-headless.min.js: src webpack.config.js stamp-npm
./node_modules/.bin/npx webpack --mode=production --type=headless
dist/converse-no-dependencies.js: src webpack.config.js stamp-npm
./node_modules/.bin/npx webpack --mode=development --type=nodeps
dist/converse-no-dependencies.min.js: src webpack.config.js stamp-npm
./node_modules/.bin/npx webpack --mode=production --type=nodeps
dist/converse-no-dependencies-es2015.js: src webpack.config.js stamp-npm
./node_modules/.bin/npx webpack --mode=development --type=nodeps --lang=es2015
dist/converse.js: transpile src stamp-npm
$(RJS) -o src/build.js include=converse out=dist/converse.js optimize=none
dist/converse.min.js: transpile src stamp-npm
$(RJS) -o src/build.js include=converse out=dist/converse.min.js
dist/converse-headless.js: transpile src stamp-npm
$(RJS) -o src/build.js paths.converse=src/headless include=converse out=dist/converse-headless.js optimize=none
dist/converse-headless.min.js: transpile src stamp-npm
$(RJS) -o src/build.js paths.converse=src/headless include=converse out=dist/converse-headless.min.js
dist/converse-esnext.js: src stamp-npm
$(RJS) -o src/build-esnext.js include=converse out=dist/converse-esnext.js optimize=none
dist/converse-esnext.min.js: src stamp-npm
$(RJS) -o src/build-esnext.js include=converse out=dist/converse-esnext.min.js
dist/converse-no-dependencies.js: transpile src stamp-npm
$(RJS) -o src/build-no-dependencies.js optimize=none out=dist/converse-no-dependencies.js
dist/converse-no-dependencies.min.js: transpile src stamp-npm
$(RJS) -o src/build-no-dependencies.js out=dist/converse-no-dependencies.min.js
.PHONY: dist
dist:: build
.PHONY: build
build:: dev css transpile $(BUILDS)
build:: dev css $(BUILDS)
########################################################################
## Tests

View File

@ -1,210 +0,0 @@
##############################################################################
#
# Copyright (c) 2006 Zope Foundation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""Bootstrap a buildout-based project
Simply run this script in a directory containing a buildout.cfg.
The script accepts buildout command-line options, so you can
use the -c option to specify an alternate configuration file.
"""
import os
import shutil
import sys
import tempfile
from optparse import OptionParser
__version__ = '2015-07-01'
# See zc.buildout's changelog if this version is up to date.
tmpeggs = tempfile.mkdtemp(prefix='bootstrap-')
usage = '''\
[DESIRED PYTHON FOR BUILDOUT] bootstrap.py [options]
Bootstraps a buildout-based project.
Simply run this script in a directory containing a buildout.cfg, using the
Python that you want bin/buildout to use.
Note that by using --find-links to point to local resources, you can keep
this script from going over the network.
'''
parser = OptionParser(usage=usage)
parser.add_option("--version",
action="store_true", default=False,
help=("Return bootstrap.py version."))
parser.add_option("-t", "--accept-buildout-test-releases",
dest='accept_buildout_test_releases',
action="store_true", default=False,
help=("Normally, if you do not specify a --version, the "
"bootstrap script and buildout gets the newest "
"*final* versions of zc.buildout and its recipes and "
"extensions for you. If you use this flag, "
"bootstrap and buildout will get the newest releases "
"even if they are alphas or betas."))
parser.add_option("-c", "--config-file",
help=("Specify the path to the buildout configuration "
"file to be used."))
parser.add_option("-f", "--find-links",
help=("Specify a URL to search for buildout releases"))
parser.add_option("--allow-site-packages",
action="store_true", default=False,
help=("Let bootstrap.py use existing site packages"))
parser.add_option("--buildout-version",
help="Use a specific zc.buildout version")
parser.add_option("--setuptools-version",
help="Use a specific setuptools version")
parser.add_option("--setuptools-to-dir",
help=("Allow for re-use of existing directory of "
"setuptools versions"))
options, args = parser.parse_args()
if options.version:
print("bootstrap.py version %s" % __version__)
sys.exit(0)
######################################################################
# load/install setuptools
try:
from urllib.request import urlopen
except ImportError:
from urllib2 import urlopen
ez = {}
if os.path.exists('ez_setup.py'):
exec(open('ez_setup.py').read(), ez)
else:
exec(urlopen('https://bootstrap.pypa.io/ez_setup.py').read(), ez)
if not options.allow_site_packages:
# ez_setup imports site, which adds site packages
# this will remove them from the path to ensure that incompatible versions
# of setuptools are not in the path
import site
# inside a virtualenv, there is no 'getsitepackages'.
# We can't remove these reliably
if hasattr(site, 'getsitepackages'):
for sitepackage_path in site.getsitepackages():
# Strip all site-packages directories from sys.path that
# are not sys.prefix; this is because on Windows
# sys.prefix is a site-package directory.
if sitepackage_path != sys.prefix:
sys.path[:] = [x for x in sys.path
if sitepackage_path not in x]
setup_args = dict(to_dir=tmpeggs, download_delay=0)
if options.setuptools_version is not None:
setup_args['version'] = options.setuptools_version
if options.setuptools_to_dir is not None:
setup_args['to_dir'] = options.setuptools_to_dir
ez['use_setuptools'](**setup_args)
import setuptools
import pkg_resources
# This does not (always?) update the default working set. We will
# do it.
for path in sys.path:
if path not in pkg_resources.working_set.entries:
pkg_resources.working_set.add_entry(path)
######################################################################
# Install buildout
ws = pkg_resources.working_set
setuptools_path = ws.find(
pkg_resources.Requirement.parse('setuptools')).location
# Fix sys.path here as easy_install.pth added before PYTHONPATH
cmd = [sys.executable, '-c',
'import sys; sys.path[0:0] = [%r]; ' % setuptools_path +
'from setuptools.command.easy_install import main; main()',
'-mZqNxd', tmpeggs]
find_links = os.environ.get(
'bootstrap-testing-find-links',
options.find_links or
('http://downloads.buildout.org/'
if options.accept_buildout_test_releases else None)
)
if find_links:
cmd.extend(['-f', find_links])
requirement = 'zc.buildout'
version = options.buildout_version
if version is None and not options.accept_buildout_test_releases:
# Figure out the most recent final version of zc.buildout.
import setuptools.package_index
_final_parts = '*final-', '*final'
def _final_version(parsed_version):
try:
return not parsed_version.is_prerelease
except AttributeError:
# Older setuptools
for part in parsed_version:
if (part[:1] == '*') and (part not in _final_parts):
return False
return True
index = setuptools.package_index.PackageIndex(
search_path=[setuptools_path])
if find_links:
index.add_find_links((find_links,))
req = pkg_resources.Requirement.parse(requirement)
if index.obtain(req) is not None:
best = []
bestv = None
for dist in index[req.project_name]:
distv = dist.parsed_version
if _final_version(distv):
if bestv is None or distv > bestv:
best = [dist]
bestv = distv
elif distv == bestv:
best.append(dist)
if best:
best.sort()
version = best[-1].version
if version:
requirement = '=='.join((requirement, version))
cmd.append(requirement)
import subprocess
if subprocess.call(cmd) != 0:
raise Exception(
"Failed to execute command:\n%s" % repr(cmd)[1:-1])
######################################################################
# Import and run buildout
ws.add_entry(tmpeggs)
ws.require(requirement)
import zc.buildout.buildout
if not [a for a in args if '=' not in a]:
args.append('bootstrap')
# if -c was provided, we push it back into args for buildout' main function
if options.config_file is not None:
args[0:0] = ['-c', options.config_file]
zc.buildout.buildout.main(args)
shutil.rmtree(tmpeggs)

View File

@ -11,12 +11,4 @@ eggs =
sphinx-bootstrap-theme
[versions]
docutils = 0.13.1
Jinja2 = 2.9.5
MarkupSafe = 0.23
Pygments = 2.2.0
six = 1.10.0
setuptools = 28.6.1
Sphinx = 1.5.2
z3c.recipe.egg = 2.0.3
zc.buildout = 2.5.3
Sphinx = 1.7.5

View File

@ -1,3 +0,0 @@
This directory exists as a location for intermediate files generated by the
Babel compiler, before they're bundled into distribution bundles in the
`./dist/` directory.

File diff suppressed because it is too large Load Diff

26
css/fullpage.css Normal file
View File

@ -0,0 +1,26 @@
body {
font-family: "Lora", "Helvetica Neue", Helvetica, Arial, sans-serif;
color: #ffffff;
background-color: #578EA9;
}
.brand-heading {
font-size: 600%;
margin-left: -10%;
}
.icon-conversejs {
font-size: 88%;
}
.brand-heading div.content {
height: 100vh;
width: 100vw;
position: fixed;
background-color: #578EA9;
}
.brand-heading div.content .inner-content {
text-align: center;
padding: 7%;
}
p.no-chats {
padding-right: 10%;
font-size: 120%;
}

File diff suppressed because it is too large Load Diff

View File

@ -117,6 +117,14 @@ a:hover, a:focus {
.outro {
background: url("images/bgtr.svg") top right no-repeat, url("images/bgbl.svg") bottom left no-repeat, url("images/overlay.png"), linear-gradient(45deg, #384955, #655361, #85505f); }
section h2 {
color: #E7A151; }
section h3 {
color: #89B7CD; }
section h4 {
color: #5CBC86;
font-size: 1.5em; }
.brand-heading {
font-family: Futura,Helvetica,Trebuchet MS,Arial,sans-serif;
font-weight: normal;
@ -181,7 +189,17 @@ a:hover, a:focus {
-moz-animation-timing-function: linear; }
.content-section {
padding-top: 100px; }
padding-top: 100px;
padding-top: 100px;
min-height: 100vh; }
.content-section .privacy-policy {
padding-top: 2em; }
.content-section .privacy-policy h4 {
padding-top: 1.5em; }
.content-section .privacy-policy p {
font-size: 1.2em;
padding-bottom: 0;
margin-bottom: 1em; }
.donate-section {
width: 100%;
@ -194,7 +212,6 @@ a:hover, a:focus {
@media (min-width: 767px) {
.content-section {
padding-top: 150px;
padding-bottom: 50px; }
.donate-section {
@ -304,6 +321,8 @@ ul.features {
clear: both;
font-size: 1.4em;
padding: 2em 0 6em 0; }
.sponsors ul {
padding: 0; }
.sponsors h2 {
text-align: center; }

View File

@ -9,35 +9,33 @@
<meta name="author" content="JC Brand" />
<meta name="keywords" content="xmpp chat webchat converse.js" />
<link rel="shortcut icon" type="image/ico" href="css/images/favicon.ico"/>
<link type="text/css" rel="stylesheet" media="screen" href="css/inverse.css" />
<script src="node_modules/requirejs/require.js"></script>
<script src="src/config.js"></script>
<link type="text/css" rel="stylesheet" media="screen" href="css/fullpage.css" />
<link type="text/css" rel="stylesheet" media="screen" href="css/converse.css" />
<script src="dist/converse.js"></script>
</head>
<body class="reset">
<script>
require(['converse', 'converse-omemo'], function (converse) {
converse.initialize({
auto_away: 300,
i18n: 'en',
// auto_join_rooms: [
// 'discuss@conference.conversejs.org',
// 'prosody@conference.prosody.im',
// 'jdev@conference.jabber.org'
// ],
// websocket_url: 'ws://chat.example.org:5280/xmpp-websocket',
view_mode: 'fullscreen',
archived_messages_page_size: '500',
allow_public_bookmarks: true,
notify_all_room_messages: [
'discuss@conference.conversejs.org'
],
// bosh_service_url: 'http://chat.example.org:5280/http-bind/',
bosh_service_url: 'https://conversejs.org/http-bind/', // Please use this connection manager only for testing purposes
message_archiving: 'always',
debug: true
});
converse.initialize({
auto_away: 300,
i18n: 'en',
// auto_join_rooms: [
// 'discuss@conference.conversejs.org',
// 'prosody@conference.prosody.im',
// 'jdev@conference.jabber.org'
// ],
// websocket_url: 'ws://chat.example.org:5280/xmpp-websocket',
view_mode: 'fullscreen',
archived_messages_page_size: '500',
allow_public_bookmarks: true,
notify_all_room_messages: [
'discuss@conference.conversejs.org'
],
// bosh_service_url: 'http://chat.example.org:5280/http-bind/',
bosh_service_url: 'https://conversejs.org/http-bind/', // Please use this connection manager only for testing purposes
message_archiving: 'always',
debug: true
});
</script>
</body>

File diff suppressed because one or more lines are too long

103805
dist/converse.js vendored

File diff suppressed because one or more lines are too long

View File

@ -7,10 +7,10 @@ Configuration
=============
The included minified JavaScript and CSS files can be used for demoing or testing, but
you'll want to configure *Converse.js* to suit your needs before you deploy it
you'll want to configure *Converse* to suit your needs before you deploy it
on your website.
*Converse.js* is passed its configuration settings when you call its *initialize* method.
*Converse* is passed its configuration settings when you call its *initialize* method.
You'll most likely want to call the *initialize* method in your HTML page. For
an example of how this is done, please see the bottom of the *./index.html* page.
@ -18,7 +18,7 @@ an example of how this is done, please see the bottom of the *./index.html* page
Please refer to the `Configuration settings`_ section below for info on
all the available configuration settings.
After you have configured *Converse.js*, you'll have to regenerate the minified
After you have configured *Converse*, you'll have to regenerate the minified
JavaScript file so that it will include the new settings. Please refer to the
:ref:`minification` section for more info on how to do this.
@ -83,7 +83,7 @@ requiring them to log in manually.
When a BOSH session is initially created, you'll receive three tokens.
A JID (jabber ID), SID (session ID) and RID (Request ID).
Converse.js needs these tokens in order to attach to that same session.
Converse needs these tokens in order to attach to that same session.
There are two complementary configuration settings to ``prebind``.
They are :ref:`keepalive` and `prebind_url`_.
@ -200,7 +200,7 @@ allow_public_bookmarks
Some XMPP servers don't support private PEP/PubSub nodes, as required for
private bookmarks and outlined in `XEP-0223 <https://xmpp.org/extensions/xep-0223.html>`_.
Even though Converse.js asks for the bookmarks to be kept private (via the
Even though Converse asks for the bookmarks to be kept private (via the
`<publish-options>` XML node), the server simply ignores the privacy settings
and publishes the node contents under the default privacy setting, which makes
the information available to all roster contacts.
@ -376,13 +376,13 @@ For example::
blacklisted_plugins
-------------------
* Default: ``[]`` (``['converse-minimize', 'converse-dragresize']`` for inVerse)
* Default: ``[]``
A list of plugin names that are blacklisted and will therefore not be
initialized once ``converse.initialize`` is called, even if the same plugin is
whitelisted.
From Converse.js 3.0 onwards most of the API is available only to plugins and
From Converse 3.0 onwards most of the API is available only to plugins and
all plugins need to be whitelisted first.
The usecase for blacklisting is generally to disable removed core plugins
@ -495,7 +495,7 @@ connection_options
* Default: ``{}``
* Type: Object
Converse.js relies on `Strophe.js <http://strophe.im>`_ to establish and
Converse relies on `Strophe.js <http://strophe.im>`_ to establish and
maintain a connection to the XMPP server.
This option allows you to pass a map of configuration options to be passed into
@ -672,7 +672,7 @@ geouri_regex
Regular expression used to extract geo coordinates from links to openstreetmap.
geouri_replacement
----------------
------------------
* Default: ``'https://www.openstreetmap.org/?mlat=$1&mlon=$2#map=18/$1/$2'``
@ -698,7 +698,7 @@ If set to ``true``, then don't show offline users.
hide_open_bookmarks
-------------------
* Default: ``false`` (``true`` for inVerse).
* Default: ``false`` (``true`` when the ``view_mode`` is set to ``fullscreen``).
This setting applies to the ``converse-bookmarks`` plugin and specfically the
list of bookmarks shown in the ``Rooms`` tab of the control box.
@ -711,48 +711,6 @@ Makes sense to set this to ``true`` when also using the non-core
``converse-roomslist`` plugin, which shows a list of currently open (i.e.
"joined") rooms.
include_offline_state
---------------------
* Default: `false`
Originally, converse.js included an `offline` state which the user could
choose (along with `online`, `busy` and `away`).
Eventually it was however decided to remove this state, since the `offline`
state doesn't propagate across tabs like the others do.
What's meant by "propagate across tabs", is that when you set the state to
`offline` in one tab, and you have instances of converse.js open in other tabs
in your browser, then those instances will not have their states changed to
`offline` as well. For the other statees the change is however propagated.
The reason for this is that according to the XMPP spec, there is no `offline`
state. The only defined states are:
* away -- The entity or resource is temporarily away.
* chat -- The entity or resource is actively interested in chattiIng.
* dnd -- The entity or resource is busy (dnd = "Do Not Disturb").
* xa -- The entity or resource is away for an extended period (xa = "eXtended Away").
Read the `relevant section in the XMPP spec <https://xmpp.org/rfcs/rfc6121.html#presence-syntax-children-show>`_
for more info.
What used to happen in converse.js when the `offline` state was chosen, is
that a presence stanza with a `type` of `unavailable` was sent out.
This is actually exactly what happens when you log out of converse.js as well,
with the notable exception that in the `offline` state, the connection is not
terminated. So you can at any time change your state to something else and
start chatting again.
This might be useful to people, however the fact that the `offline` state
doesn't propagate across tabs means that the user experience is inconsistent,
confusing and appears "broken".
If you are however aware of this issue and still want to allow the `offline`
state, then you can set this option to `true` to enable it.
.. _`i18n`:
i18n
@ -767,7 +725,7 @@ The translations for that locale must be available in JSON format at the
If an explicit locale is specified via the ``i18n`` setting and the
translations for that locale are not found at the `locales_url``, then
then Converse.js will fall back to trying to determine the browser's language
then Converse will fall back to trying to determine the browser's language
and fetching those translations, or if that fails the default English texts
will be used.
@ -789,7 +747,7 @@ keepalive
* Default: ``true``
Determines whether Converse.js will maintain the chat session across page
Determines whether Converse will maintain the chat session across page
loads.
This setting should also be used in conjunction with ``authentication`` set to `prebind`_.
@ -827,7 +785,7 @@ locales
'ru', 'uk', 'zh'
]
This setting restricts the locales that are supported by Converse.js and
This setting restricts the locales that are supported by Converse and
therefore what may be given as value for the :ref:`i18n` option.
Any other locales will be ignored.
@ -842,7 +800,7 @@ locales_url
* Default: ``/locale/{{{locale}}}/LC_MESSAGES/converse.json``,
The URL from where Converse.js should fetch translation JSON.
The URL from where Converse should fetch translation JSON.
The three curly braces ``{{{ }}}`` are
`Mustache <https://github.com/janl/mustache.js#readme>`_-style
@ -854,7 +812,7 @@ The variable being interpolated via the curly braces is ``locale``, which is
the value passed in to the `i18n`_ setting, or the browser's locale or the
default local or `en` (resolved in that order).
From version 3.3.0, Converse.js no longer bundles all translations into its
From version 3.3.0, Converse no longer bundles all translations into its
final build file. Instead, only the relevant translations are fetched at
runtime.
@ -995,7 +953,7 @@ muc_show_join_leave
* Default; ``true``
Determines whether Converse.js will show info messages inside a chatroom
Determines whether Converse will show info messages inside a chatroom
whenever a user joins or leaves it.
nickname
@ -1028,6 +986,39 @@ This option specifies which icon is shown in HTML5 notifications, as provided
by the ``src/converse-notification.js`` plugin.
oauth_providers
---------------
* Default: ``[]``
Allows you to specify a list of OAuth providers that the user may use to log in
with.
.. note::
Your XMPP server will have to support Oauth logins
.. code-block:: javascript
converse.initialize({
oauth_providers: {
'github': {
'client_id': '1338d9f7ff52b1309b29',
'host': 'chat.example.org',
'class': 'fa-github-alt',
'id': 'github',
'name': 'Github'
},
'twitter': {
'client_id': '0332d98cff83b1999b22',
'host': 'chat.example.org',
'class': 'fa-twitter',
'id': 'twitter',
'name': 'Twitter'
}
},
});
ping_interval
-------------
@ -1049,7 +1040,7 @@ play_sounds
Plays a notification sound when you receive a personal message or when your
nickname is mentioned in a chatroom.
Inside the ``./sounds`` directory of the Converse.js repo you'll see MP3 and Ogg
Inside the ``./sounds`` directory of the Converse repo you'll see MP3 and Ogg
formatted sound files. We need both, because neither format is supported by all browsers.
You can set the URL where the sound files are hosted with the `sounds_path`_
@ -1089,12 +1080,12 @@ priority
* Type: Number
Determines the priority used for presence stanzas sent out from this resource
(i.e. this instance of Converse.js).
(i.e. this instance of Converse).
The priority of a given XMPP chat client determines the importance of its presence
stanzas in relation to stanzas received from other clients of the same user.
In Converse.js, the indicated chat status of a roster contact will be taken from the
In Converse, the indicated chat status of a roster contact will be taken from the
presence stanza (and associated resource) with the highest priority.
If multiple resources have the same top priority, then the chat status will be
@ -1110,6 +1101,39 @@ providers_link
The hyperlink on the registration form which points to a directory of public
XMPP servers.
push_app_servers
----------------
* Default: ``[]``
This option lets you enable or disable so-called push notification "App Servers"
(as per `XEP-0357 <https://xmpp.org/extensions/xep-0357.html>`_).
For each "App Server" an object needs to be passed in. When enabling, you need
to specify ``jid`` and ``node`` values. You can also provide a
``secret``, if required by your App Server.
When disabling, you need to specify at least a ``jid`` and set ``disabled`` to
``true``. This will disable notifications to all pubsub nodes on that "App
Server". If you want to disable only a particular node, then specify a ``node``
value as well.
For example:
.. code-block:: javascript
converse.initialize({
'push_app_servers': [{
'jid': 'push-4@client.example',
'node': 'yxs32uqsflafdk3iuqo',
'disable': true
}, {
'jid': 'push-5@client.example',
'node': 'yxs32uqsflafdk3iuqo',
}]
});
root
----
@ -1150,7 +1174,7 @@ configured.
.. note::
It's currently not possible to use converse.js to assign contacts to groups.
Converse.js can only show users and groups that were previously configured
Converse can only show users and groups that were previously configured
elsewhere.
show_chatstate_notifications
@ -1163,7 +1187,7 @@ Specifies whether chat state (online, dnd, away) HTML5 desktop notifications sho
show_controlbox_by_default
--------------------------
* Default: ``false`` (``true`` for inVerse)
* Default: ``false`` (``true`` when the ``view_mode`` is set to ``fullscreen``)
The "controlbox" refers to the special chatbox containing your contacts roster,
status widget, chatrooms and other controls.
@ -1280,7 +1304,7 @@ See also `trusted`_.
sticky_controlbox
-----------------
* Default: ``false`` (``true`` for inVerse).
* Default: ``false`` (``true`` when the ``view_mode`` is set to ``fullscreen``).
If set to ``true``, the control box (which includes the login, registration,
contacts and rooms tabs) will not be closeable. It won't have a close button at
@ -1304,10 +1328,10 @@ loaded), then an error will be raised.
Otherwise a message will simply be logged and the override instruction ignored.
The Converse.js plugins architecture can have an :ref:`dependencies`
The Converse plugins architecture can have an :ref:`dependencies`
plugin attribute. This enables you to specify an array of other plugins which
this one depends on.
Converse.js (more specifically, `pluggable.js <https://jcbrand.github.io/pluggable.js/>`_)
Converse (more specifically, `pluggable.js <https://jcbrand.github.io/pluggable.js/>`_)
will first load these dependencies before executing the plugin's overrides and
calling its ``initialize`` method.
@ -1344,7 +1368,7 @@ This setting determines whether the default value of the "This is a trusted devi
When the current device is not trusted, then localStorage and sessionStorage
will be cleared when the user logs out, thereby removing all cached data.
Clearing the cache in this way makes Converse.js much slower when the user logs
Clearing the cache in this way makes Converse much slower when the user logs
in again, because all data needs to be fetch anew.
See also `storage`_.
@ -1365,7 +1389,7 @@ use_otr_by_default
* Default: ``false``
If set to ``true``, Converse.js will automatically try to initiate an OTR (off-the-record)
If set to ``true``, Converse will automatically try to initiate an OTR (off-the-record)
encrypted chat session every time you open a chatbox.
visible_toolbar_buttons
@ -1424,7 +1448,7 @@ support.
configuration setting).
.. note::
Converse.js does not yet support "keepalive" with websockets.
Converse does not yet support "keepalive" with websockets.
.. _`view_mode`:
@ -1468,6 +1492,9 @@ Since version 3.3.0, the ``inverse.js`` and ``converse-mobile.js`` builds no
longer exist. Instead the standard ``converse.js`` build is used, together with
the appropriate ``view_mode`` value.
Since verseion 4.0.0, there is now also only one CSS file to be used for all
the different view modes, ``converse.css``.
The ``converse-muc-embedded.js`` build is still kept, because it's smaller than
``converse.js`` due to unused code being removed. It doesn't however contain
any new code, so the full ``converse.js`` build could be used instead, as long
@ -1476,19 +1503,6 @@ as ``view_mode`` is set to ``embedded``.
Furthermore, it's no longer necessary to whitelist or blacklist any plugins
when switching view modes.
.. note::
Although the ``view_mode`` setting has removed the need for different
JavaScript builds, you'll still need to use different CSS files depending
on the view mode.
* For ``embedded`` you need to use ``./css/converse-muc-embedded.css``
* For ``fullscreen`` you need ``./css/inverse.css``
* For ``mobile`` you need to use both ``./css/converse.css`` and ``./css/mobile.css``
* For ``overlayed`` this is ``./css/converse.css``
Hopefully in a future release the CSS files will be combined and you'll
only need ``converse.css``
.. _`whitelisted_plugins`:
@ -1500,7 +1514,7 @@ whitelisted_plugins
A list of plugin names that are whitelisted and will therefore be
initialized once ``converse.initialize`` is called.
From Converse.js 3.0 onwards most of the API is available only to plugins and
From Converse 3.0 onwards most of the API is available only to plugins and
all plugins need to be whitelisted first.
This is done to prevent malicious scripts from using the API to trick users or

View File

@ -7,7 +7,7 @@
Events and promises
===================
Converse.js and its plugins emit various events which you can listen to via the
Converse and its plugins emit various events which you can listen to via the
:ref:`listen-grouping`.
Some of these events are also available as `ES2015 Promises <http://es6-features.org/#PromiseUsage>`_,
@ -294,24 +294,6 @@ Also available as an `ES2015 Promise <http://es6-features.org/#PromiseUsage>`_:
// Your code here...
});
reconnecting
~~~~~~~~~~~~
Fired once converse.js has determined that it will attempt to reconnect (and
each subsequent time, if it attempts repeatedly).
reconnected
~~~~~~~~~~~
After the connection has dropped and converse.js has reconnected.
Any Strophe stanza handlers (as registered via `converse.listen.stanza`) will
have to be registered anew.
.. code-block:: javascript
_converse.api.listen.on('reconnected', function () { ... });
privateChatsAutoJoined
~~~~~~~~~~~~~~~~~~~~~~
@ -330,6 +312,33 @@ Also available as an `ES2015 Promise <http://es6-features.org/#PromiseUsage>`_.
// Your code here...
});
reconnecting
~~~~~~~~~~~~
Fired once converse.js has determined that it will attempt to reconnect (and
each subsequent time, if it attempts repeatedly).
reconnected
~~~~~~~~~~~
After the connection has dropped and converse.js has reconnected.
Any Strophe stanza handlers (as registered via `converse.listen.stanza`) will
have to be registered anew.
.. code-block:: javascript
_converse.api.listen.on('reconnected', function () { ... });
registeredGlobalEventHandlers
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Called once Converse has registered its global event handlers (for events such
as window resize or unload).
Plugins can listen to this event as cue to register their own global event
handlers.
roomsAutoJoined
---------------
@ -466,6 +475,14 @@ Similar to `rosterInitialized`, but instead pertaining to reconnection. This
event indicates that the Backbone collections representing the roster and its
groups are now again available after converse.js has reconnected.
serviceDiscovered
~~~~~~~~~~~~~~~~~
When converse.js has learned of a service provided by the XMPP server. See XEP-0030.
``_converse.api.listen.on('serviceDiscovered', function (service) { ... });``
.. _`statusInitialized`:
statusInitialized
@ -497,12 +514,12 @@ When own custom status message has changed.
``_converse.api.listen.on('statusMessageChanged', function (message) { ... });``
serviceDiscovered
~~~~~~~~~~~~~~~~~
streamFeaturesAdded
~~~~~~~~~~~~~~~~~~~
When converse.js has learned of a service provided by the XMPP server. See XEP-0030.
``_converse.api.listen.on('serviceDiscovered', function (service) { ... });``
Emitted as soon as Converse has processed the stream features as advertised by
the server. If you want to check whether a stream feature is supported before
proceeding, then you'll first want to wait for this event.
windowStateChanged
~~~~~~~~~~~~~~~~~~

View File

@ -19,7 +19,7 @@ and a private chat with a URL fragment such as
Off-the-record encryption
=========================
Converse.js supports `Off-the-record (OTR) <https://otr.cypherpunks.ca/>`_
Converse supports `Off-the-record (OTR) <https://otr.cypherpunks.ca/>`_
encrypted messaging.
The OTR protocol not only **encrypts your messages**, it provides ways to
@ -38,17 +38,17 @@ secure crypto.
For harsh but fairly valid criticism of JavaScript cryptography, read:
`JavaScript Cryptography Considered Harmful <http://www.matasano.com/articles/javascript-cryptography/>`_.
To get an idea on how this applies to OTR support in Converse.js, please read
To get an idea on how this applies to OTR support in Converse, please read
`my thoughts on it <https://opkode.com/media/blog/2013/11/11/conversejs-otr-support>`_.
For now, suffice to say that although its useful to have OTR support in
Converse.js in order to avoid most eavesdroppers, if you need serious
Converse in order to avoid most eavesdroppers, if you need serious
communications privacy, then you're much better off using native software.
Notifications
=============
From version 0.8.1 Converse.js can play a sound notification when you receive a
From version 0.8.1 Converse can play a sound notification when you receive a
message.
For more info, refer to the :ref:`play-sounds` configuration setting.
@ -61,13 +61,10 @@ For more info, refer to the :ref:`show-desktop-notifications` configuration sett
Multilingual Support
====================
Converse.js is translated into multiple languages. The default build,
``converse.min.js``, includes all languages.
Languages increase the size of the Converse.js significantly.
If you only need one, or a subset of the available languages, it's better to
make a custom build which includes only those languages that you need.
Converse is translated into multiple languages. Translations are supplied in
JSON format and are loaded on demand. Converse will expect to find the
translations in the ``/locales`` path of your site. This can be changed via the
:ref:`locales-url` configuration setting.
Moderating chatrooms
====================
@ -103,7 +100,7 @@ Here are the different commands that may be used to moderate a chatroom:
Passwordless login with client certificates
===========================================
Converse.js supports the SASL-EXTERNAL authentication mechanism, which can be
Converse supports the SASL-EXTERNAL authentication mechanism, which can be
used together with x509 client certificates to enable passwordless login or
even 2-factor authentication.

View File

@ -34,8 +34,6 @@ via the *script* and *link* tags:
<script src="https://cdn.conversejs.org/dist/converse.min.js" charset="utf-8"></script>
.. note:: For the fullscreen :ref:`view_mode` version of converse.js, replace ``converse.min.css`` with ``inverse.min.css``.
.. note:: Instead of always loading the latest version of Converse.js via the
CDN, it's generally better to load a specific version (preferably the
latest one), to avoid breakage when new backwards-incompatible versions are
@ -111,19 +109,13 @@ split up into two parts, with the UI part dropped for this build.
Fullscreen version
------------------
Converse.js also comes in a fullscreen version (often referred to as Inverse).
Converse.js also comes in a fullscreen version.
A hosted version is available online at `inverse.chat <https://inverse.chat>`_.
Originally this version was available as a separate build file, but
as of version 4.0.0 and higher, the difference between the "overlay" and the
"fullscreen" versions of converse.js is simply a matter of configuring the
:ref:`view_mode` and including the right CSS file.
For the default "overlay" version, ``converse.css`` is used, and for the
"fullscreen" version ``inverse.css`` is used.
We'd like to eventually not require two different CSS files, and to allow you
to seamlessly switch between the different view modes.
:ref:`view_mode`.
To generate the headless build, run ``make dist/converse-headless.js`` and/or
``make dist/converse-headless.min.js``.

View File

@ -3,11 +3,12 @@
<head>
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title>inVerse</title>
<title>Converse</title>
<link rel="shortcut icon" type="image/ico" href="css/images/favicon.ico"/>
<script type="text/javascript" src="inverse-analytics.js"></script>
<noscript><p><img src="//stats.opkode.com/piwik.php?idsite=5" style="border:0;" alt="" /></p></noscript>
<link type="text/css" rel="stylesheet" media="screen" href="css/inverse.css" />
<link type="text/css" rel="stylesheet" media="screen" href="css/converse.css" />
<link type="text/css" rel="stylesheet" media="screen" href="css/fullpage.css" />
<script src="dist/converse.js"></script>
</head>
<body class="reset">

View File

@ -5,7 +5,7 @@
<meta charset="utf-8">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="description" content="Converse.js: An XMPP chat client which can be integrated into any website" />
<meta name="description" content="Converse: An XMPP chat client which can be integrated into any website" />
<meta name="author" content="JC Brand" />
<meta name="keywords" content="xmpp chat webchat converse.js" />
@ -56,8 +56,8 @@
<li class="page-scroll">
<a href="#sponsors">Sponsor</a>
</li>
<li>
<a href="/docs/html/manual.html">User Manual</a>
<li class="page-scroll">
<a href="#hosting">Hosting</a>
</li>
<li>
<a href="/docs/html/index.html">Documentation</a>
@ -107,7 +107,7 @@
<div class="col-lg-8 col-lg-offset-2">
<h2>Converse is written in JavaScript and runs in your browser.</h2>
<p>You can start using it here immediately, or you can <a href="/docs/html/index.html">integrate it into your own website</a>.</p>
<p>Take a look at the <a href="/demo">demo page</a> for other examples of how Converse.js can be configured and used.</a>
<p>Take a look at the <a href="/demo">demo page</a> for other examples of how Converse can be configured and used.</a>
<p>
You can connect to any publically accessible <a href="http://xmpp.org" target="_blank" rel="noopener">XMPP/Jabber</a> server,
@ -193,7 +193,7 @@
or <a href="https://mastodon.xyz/@jcbrand" target="_blank" rel="noopener">Mastodon</a>
<li>Chat with me via XMPP at <a href="xmpp:jc@opkode.com" class="xmpp JSnocheck" title="XMPP/Jabber">jc@opkode.com</a></li>
<li>For technical support, you can ask on <a href="http://stackoverflow.com/questions/tagged/converse.js">Stack Overflow</a>
<li>The Converse.js XMPP chatroom: <a href="xmpp:discuss@conference.conversejs.org" class="xmpp JSnocheck" title="Converse.js chat room">discuss@conference.conversejs.org</a>.</li>
<li>The Converse XMPP chatroom: <a href="xmpp:discuss@conference.conversejs.org" class="xmpp JSnocheck" title="Converse chat room">discuss@conference.conversejs.org</a>.</li>
<li>Please file bugs and feature requests on <a target="_blank" rel="noopener" href="https://github.com/jcbrand/converse.js/issues">Github</a>.</li>
</ul>
</div>
@ -217,17 +217,19 @@
<div class="row">
<div class="col-lg-8 col-lg-offset-2" style="margin-top: 3em">
<div class="sponsors">
<h2>Converse.js is supported by:</h2>
<h2>Converse is supported by:</h2>
<ul>
<li><a href="https://www.keycdn.com/" target="_blank" rel="noopener"><img style="height: 3em" src="/logo/keycdn.svg" alt="KeyCDN"></a></li>
<li><a href="https://wikisuite.org" target="_blank" rel="noopener"><img style="height: 4em" src="/logo/wikisuite-white.png" alt="WikiSuite"></a></li>
<li><a href="https://www.keycdn.com/?utm_source=conversejs" target="_blank" rel="noopener"><img style="height: 3em" src="/logo/keycdn.svg" alt="KeyCDN"></a></li>
<li><a href="https://wikisuite.org/?utm_source=conversejs" target="_blank" rel="noopener"><img style="height: 4em" src="/logo/wikisuite-white.png" alt="WikiSuite"></a></li>
<li><a href="https://hostpresto.com/?utm_source=conversejs" target="_blank" rel="noopener"><img style="height: 3em" src="/logo/hostpresto.png" alt="HostPresto"></a></li>
</ul>
</div>
<p class="sponsors-text">Converse.js is a software commons; available at no cost to you or anyone else.
Sponsorships allow us to fund further development and improvements and are greatly appreciated.
If you'd like to sponsor this project, please visit <a href="https://www.patreon.com/jcbrand" target="_blank" rel="noopener">Patreon</a>
or <a href="https://liberapay.com/jcbrand" target="_blank" rel="noopener">Liberapay</a>.
<p class="sponsors-text">Converse is a software commons, available at no cost to you or anyone else.
Sponsorships allow us to fund further development and improvements.
If you'd like to sponsor this project, please visit <a href="https://www.patreon.com/jcbrand" target="_blank" rel="noopener">Patreon</a>,
<a href="https://liberapay.com/jcbrand" target="_blank" rel="noopener">Liberapay</a> or
<a href="http://opkode.com/contact" target="_blank" rel="noopener">contact us</a>.
</p>
</div>
</div>
@ -235,6 +237,93 @@
</section>
</body>
<section class="outro content-section text-center" id="hosting">
<div class="container">
<div class="row">
<div class="col-lg-8 col-lg-offset-2" style="margin-top: 3em">
<h2>XMPP Account Hosting</h2>
<p>
We provide free XMPP accounts under the domain <strong>conversejs.org</strong>.
You can create an account directly through the app on
this website or on <a href="https://inverse.chat" target="_blank" rel="noopener">inverse.chat</a>.
</p>
<p>
If you're interested in professional XMPP hosting under your
own domain name, please <a href="http://opkode.com/contact" target="_blank" rel="noopener">contact us</a>.
</p>
<div class="privacy-policy">
<h3>Privacy policy and GDPR compliance</h3>
<p>
This service is provided on a pro bono basis. An email
address is not needed to sign up and we don't sell or
monetize any of your data.
</p>
<h4>Sharing of data with 3rd parties</h4>
<p>
We don't share any of your data with 3rd parties,
except when necessary to run the service. For example,
when you send a message to a user on a differerent XMPP
server.
Your presence information (whether you're online or
not) is shared with contacts that you've added from
other servers.
</p>
<p>
Users on other XMPP servers
can request access to your (optionally filled-in) VCard data.
You can remove your VCard data through an XMPP client.
The latest version of Converse supports this, and you can
<a href="https://conversejs.org/4.0.0-alpha/fullscreen.html" target="_blank" rel="noopener">use it here</a>.
</p>
<h4>Data storage</h4>
<p>
Our XMPP server runs in a Hetzner data centre in
Strasbourg, France.
</p>
<p>
Your chat messages are archived for a period of 1
month, after which they are deleted.
</p>
<p>
Currently the <strong>conversejs.org</strong> XMPP
server does not support HTTP-file upload, which means
that we don't host any uploaded files of users.
</p>
<p>
During normal operations we don't log or process IP
addresses, although it might be necessary in certain
cases where a problem needs to be debugged (hasn't
happened yet). Logs older than 6 months are deleted.
</p>
<h4>Data portability</h4>
<p>
Currently there is no standardized way to move a
user account from one XMPP server to another.
</p>
<p>
If you'd like to have a copy of your data for
transferal to another account, please <a href="http://opkode.com/contact" target="_blank" rel="noopener">contact us</a>.
</p>
<h4>Account deletion</h4>
<p>
Currently it's not possible to automatically delete
your account via Converse, although you might be
able to do so via other XMPP clients that support
account deletion via
<a href="https://xmpp.org/extensions/xep-0077.html" target="_blank" rel="noopener">XEP-0077</a>.
</p>
<p>
You can always <a href="http://opkode.com/contact" target="_blank" rel="noopener">contact us</a>
and we'll delete your account manually.
</p>
</div>
</div>
</div>
</div>
</section>
</body>
<script>
/*
@licstart

File diff suppressed because it is too large Load Diff

BIN
logo/hostpresto.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

View File

@ -5,7 +5,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Converse.js Mockups</title>
<link type="text/css" rel="stylesheet" media="screen" href="../../node_modules/font-awesome/css/font-awesome.css" />
<link type="text/css" rel="stylesheet" media="screen" href="../../css/inverse.css" />
<link type="text/css" rel="stylesheet" media="screen" href="../../css/converse.css" />
</head>
<body class="reset">

View File

@ -5,7 +5,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Chatroom Fullscreen</title>
<link type="text/css" rel="stylesheet" media="screen" href="../../node_modules/font-awesome/css/font-awesome.css" />
<link type="text/css" rel="stylesheet" media="screen" href="../../css/inverse.css" />
<link type="text/css" rel="stylesheet" media="screen" href="../../css/converse.css" />
</head>
<body class="reset">
@ -23,12 +23,11 @@
<div class="chatbox chatroom" id="chatroom">
<div class="flyout box-flyout">
<div class="chat-head chat-head-chatroom row no-gutters">
<div class="col col-9">
<div class="chatbox-title">
<div class="chat-title">Capulet's orchard</div>
<p class="chatroom-description">Two households, both alike in dignity, In fair Verona, where we lay our scene.</p>
</div>
<div class="chatbox-buttons row no-gutters">
<a class="chatbox-btn fa fa-minus"></a>
<a class="chatbox-btn fa fa-close"></a>
<a class="chatbox-btn fa fa-wrench"></a>
</div>

View File

@ -6,7 +6,7 @@
<title>Login Fullscreen</title>
<link type="text/css" rel="stylesheet" media="screen" href="../../node_modules/bootstrap/dist/css/bootstrap.css" />
<link type="text/css" rel="stylesheet" media="screen" href="../../node_modules/font-awesome/css/font-awesome.css" />
<link type="text/css" rel="stylesheet" media="screen" href="../../css/inverse.css" />
<link type="text/css" rel="stylesheet" media="screen" href="../../css/converse.css" />
</head>
<body>

10423
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,8 +1,8 @@
{
"name": "converse.js",
"version": "3.3.4",
"description": "Browser based XMPP instant messaging client",
"main": "main.js",
"description": "Browser based XMPP chat client",
"main": "dist/converse.js",
"directories": {
"doc": "docs",
"locale": "locale",
@ -20,20 +20,24 @@
"chatrooms",
"webchat"
],
"author": "JC Brand",
"author": {
"name": "JC Brand",
"email": "jc@opkode.com"
},
"license": "MPL-2.0",
"bugs": {
"url": "https://github.com/jcbrand/converse.js/issues"
"url": "https://github.com/conversejs/converse.js/issues"
},
"engines": {
"browser": "*"
},
"devDependencies": {
"@babel/cli": "^7.0.0-beta.35",
"@babel/core": "^7.0.0-beta.35",
"@babel/preset-env": "^7.0.0-beta.35",
"almond": "~0.3.3",
"@babel/cli": "^7.0.0-beta.48",
"@babel/core": "^7.0.0-beta.48",
"@babel/preset-env": "^7.0.0-beta.48",
"@babel/preset-es2015": "^7.0.0-beta.49",
"awesomplete-avoid-xss": "^1.1.2",
"babel-loader": "^8.0.0-beta.3",
"backbone": "1.3.3",
"backbone.browserStorage": "0.0.3",
"backbone.nativeview": "^0.3.3",
@ -48,9 +52,12 @@
"es6-promise": "^4.1.0",
"eslint": "4.19.1",
"eslint-plugin-lodash": "^2.3.3",
"exports-loader": "^0.7.0",
"filesize": "^3.6.1",
"font-awesome": "^4.7.0",
"hellojs": "^1.16.1",
"http-server": "^0.10.0",
"imports-loader": "^0.8.0",
"install": "^0.9.5",
"jasmine-core": "2.6.4",
"jed": "1.1.1",
@ -60,7 +67,10 @@
"lodash": "4.17.4",
"lodash-template-loader": "^2.0.0",
"long": "^3.1.0",
"lodash-template-webpack-loader": "jcbrand/lodash-template-webpack-loader",
"minimist": "^1.2.0",
"moment": "~> 2.19.3 ",
"npm": "^5.7.1",
"otr": "0.2.16",
"pluggable.js": "2.0.0",
"po2json": "^0.4.4",
@ -70,17 +80,15 @@
"sinon": "^2.1.0",
"sizzle": "^2.3.3",
"snabbdom": "0.7.1",
"strophe.js": "1.2.14",
"strophe.js": "1.2.15",
"strophejs-plugin-ping": "0.0.1",
"strophejs-plugin-register": "0.0.1",
"strophejs-plugin-rsm": "0.0.1",
"text": "requirejs/text#2.0.15",
"uglify-es": "^3.0.24",
"urijs": "^1.19.1",
"wait-until-promise": "^1.0.0",
"webpack": "^4.0.1",
"webpack-cli": "^2.1.4",
"xss": "^0.3.3"
},
"dependencies": {
"npm": "^5.7.1"
}
}

18
redirect.html Normal file
View File

@ -0,0 +1,18 @@
<!doctype html>
<html class="no-js" lang="en">
<head>
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title>Converse</title>
<link rel="shortcut icon" type="image/ico" href="css/images/favicon.ico"/>
<link type="text/css" rel="stylesheet" media="screen" href="css/converse.css" />
<script src="node_modules/hellojs/dist/hello.all.js"></script>
</head>
<body>
<div class="content">
<div class="inner-content">
<h1 class="brand-heading"><i class="icon-conversejs"></i> Converse</h1>
</div>
</div>
</body>
</html>

1
requirements.txt Normal file
View File

@ -0,0 +1 @@
zc.buildout==2.11.4

View File

@ -1,4 +1,3 @@
#converse-embedded-chat,
#conversejs {
[hidden] { display: none; }

View File

@ -1,7 +1,9 @@
#conversejs {
.chatbox-navback {
display: none;
}
.flyout {
border-radius: $chatbox-border-radius;
bottom: $chatbox-hover-height;
position: absolute;
@media screen and (max-height: $mobile-landscape-height) {
@ -38,7 +40,6 @@
flex-wrap: nowrap;
color: #ffffff;
font-size: 100%;
height: $chat-head-height;
margin: 0;
padding: 0.5em;
position: relative;
@ -53,6 +54,11 @@
margin-right: 0.5em;
}
.chatbox-title {
.chatroom-description {
font-size: 80%;
}
}
.chatbox-buttons {
flex-direction: row-reverse;
@include make-col-ready();
@ -120,8 +126,6 @@
justify-content: space-between;
background-color: $chat-head-color;
box-shadow: 1px 3px 5px 3px rgba(0, 0, 0, 0.4);
height: $chat-height;
min-height: $chat-height/2;
z-index: 1;
overflow-y: scroll;
width: 100%;
@ -246,7 +250,6 @@
width: 100%;
border: none;
min-height: $chat-textarea-height;
max-height: $max-chat-textarea-height;
margin-bottom: -4px; // Not clear why this is necessar :(
&.spoiler {
height: 42px;
@ -330,7 +333,6 @@
}
ul {
&.emoji-picker {
height: $emoji-picker-height;
overflow: scroll;
padding: 0.5em;
}
@ -434,33 +436,224 @@
}
}
#conversejs.converse-fullscreen {
.chatbox-btn {
font-size: $fullpage-chatbox-button-size;
}
/* ******************* Overlay and embedded styles *************************** */
#conversejs.converse-embedded,
#conversejs.converse-overlayed {
.chat-head {
border-top-left-radius: $chatbox-border-radius;
border-top-right-radius: $chatbox-border-radius;
@media screen and (max-height: $mobile-landscape-height) {
border-top-left-radius: 0;
border-top-right-radius: 0;
}
@media screen and (max-width: $mobile-portrait-length) {
border-top-left-radius: 0;
border-top-right-radius: 0;
}
.chatbox-title {
@include make-col(8);
}
.chatbox-buttons {
@include make-col(3);
@include make-col(4);
}
}
.chatbox {
min-width: $overlayed-chat-width!important;
width: $overlayed-chat-width;
.box-flyout {
min-width: $overlayed-chat-width!important;
width: $overlayed-chat-width;
}
.chat-body {
.chat-message {
line-height: $line-height-large;
.chat-msg-author {
line-height: $line-height-large;
}
.chat-msg-content {
line-height: $line-height-large;
.emojione {
margin-bottom: -5px;
}
}
}
}
}
.chatbox {
form.sendXMPPMessage {
.chat-toolbar {
li {
.toolbar-menu {
min-width: 235px;
ul {
&.emoji-toolbar {
width: 100%;
.emoji-category {
float: left;
}
}
}
}
}
}
}
}
}
@media screen and (max-width: 767px) {
#conversejs.converse-overlayed {
.flyout {
bottom: $overlayed-chatbox-hover-height;
}
.box-flyout {
height: $overlayed-chat-height;
min-height: $overlayed-chat-height/2;
}
.chat-head {
height: $overlayed-chat-head-height;
}
.chat-textarea {
max-height: $overlayed-max-chat-textarea-height;
}
.emoji-picker {
height: $overlayed-emoji-picker-height;
}
}
@include media-breakpoint-down(sm) {
#conversejs.converse-overlayed {
> .row {
flex-direction: column;
&.no-gutters {
margin: -1em;
}
}
}
}
/* ******************* Fullpage styles *************************** */
#conversejs.converse-fullscreen {
.flyout {
border-radius: 0;
border-top: 0.8em solid $chat-head-color;
border: $flyout-padding solid $chat-head-color;
bottom: 0;
}
.chatbox-btn {
font-size: $fullpage-chatbox-button-size;
margin: 0 0.3em;
}
.chat-head {
height: $fullpage-chat-head-height;
font-size: $font-size-huge;
padding: 0;
.user-custom-message {
font-size: 50%;
height: auto;
line-height: $line-height;
}
.chatbox-title {
@include make-col(10);
}
.chatbox-buttons {
@include make-col(2);
}
}
.chat-textarea {
max-height: $fullpage-max-chat-textarea-height;
}
.emoji-picker {
height: $fullpage-emoji-picker-height;
}
.chatbox {
width: 100%;
height: 100%;
margin: 0;
@include make-col-ready();
@include media-breakpoint-up(md) {
@include make-col(9);
}
@include media-breakpoint-up(lg) {
@include make-col(9);
}
@include media-breakpoint-up(xl) {
@include make-col(10);
}
.box-flyout {
background-color: $chat-head-color;
box-shadow: none;
height: $fullpage-chat-height;
min-height: $fullpage-chat-height/2;
width: $fullpage-chat-width;
}
.chat-body {
background-color: $chat-head-color;
border-top-left-radius: $chatbox-border-radius;
border-top-right-radius: $chatbox-border-radius;
.chat-message {
line-height: $line-height;
font-size: $font-size-small;
.chat-msg-author {
line-height: $line-height;
}
.chat-msg-content {
line-height: $line-height;
.emojione {
height: $line-height;
margin-bottom: -$line-height/4;
}
}
}
}
.chat-content {
border-top-left-radius: $chatbox-border-radius;
border-top-right-radius: $chatbox-border-radius;
}
.chat-title {
font-size: $font-size-huge;
line-height: $line-height-huge;
}
.sendXMPPMessage {
ul {
width: 100%;
}
.toggle-smiley {
ul {
&.emoji-toolbar {
.emoji-category-picker {
margin-right: 5em;
}
.emoji-category {
padding-left: 10px;
padding-right: 10px;
}
}
}
}
}
}
}
@include media-breakpoint-down(sm) {
#conversejs:not(.converse-embedded) {
> .row {
flex-direction: row-reverse;
}
#converse-login-panel {
.converse-form {
padding: 3em 2em 3em;
}
}
.sidebar {
display: block;
}
.chatbox {
width: calc(100% - 50px);
.row {
@ -473,4 +666,29 @@
}
}
}
#conversejs.converse-mobile,
#conversejs.converse-overlayed,
#conversejs.converse-embedded,
#conversejs.converse-fullscreen {
.chatbox {
.box-flyout {
.chatbox-navback {
display: flex;
@include make-col(2);
.fa-arrow-left {
&:before {
color: white;
}
}
}
.chatbox-title {
@include make-col(7);
}
.chatbox-buttons {
@include make-col(3);
}
}
}
}
}

View File

@ -7,12 +7,37 @@
}
}
#room-details-modal {
.features-list {
margin-left: 1em;
}
}
.chatroom-features {
width: 100%;
.features-list {
padding-top: 0;
.feature {
width: 100%;
margin-right: 0.5em;
padding-right: 0;
font-size: 1em;
cursor: help;
.fa {
margin-right: 0.5em;
color: $text-color;
}
}
}
}
.chat-head-chatroom {
background-color: $chatroom-head-color;
.chatroom-description {
color: lighten($chatroom-head-color, 25%);
font-size: $font-size-large;
font-size: $font-size;
font-size: 70%;
margin-top: 3px;
overflow-y: hidden;
@ -101,16 +126,12 @@
display: flex;
flex-direction: column;
word-wrap: break-word;
min-width: $chat-width;
.new-msgs-indicator {
background-color: $chatroom-head-color;
}
.chat-content {
height: 100%;
}
&.full {
min-width: 100%;
}
}
.occupants {
display: flex;
@ -124,21 +145,19 @@
border-bottom-right-radius: $chatbox-border-radius;
padding: 0.5em;
.occupants-heading {
font-family: $heading-font;
padding: 0.3em 0;
}
.chatroom-features {
width: 100%;
.feature {
float: left;
margin-right: 0.5em;
padding-right: 0;
font-size: 1em;
cursor: help;
.occupants-header {
display: flex;
flex-direction: column;
.hide-occupants {
align-self: flex-end;
cursor: pointer;
}
.occupants-heading {
font-family: $heading-font;
padding: 0.3em 0;
}
}
.awesomplete {
ul {
padding: 0;
@ -160,15 +179,6 @@
flex-grow: 1;
border-bottom: 1px solid lightgrey;
}
&.features-list {
padding-top: 0;
.feature {
width: 100%;
.fa {
color: $text-color;
}
}
}
li {
cursor: default;
display: block;
@ -279,14 +289,129 @@
}
}
@media screen and (max-width: 767px) {
#conversejs:not(.converse-embedded) {
.chatbox {
/* ******************* Overlay styles *************************** */
#conversejs.converse-overlayed {
.chatbox {
&.chatroom {
min-width: $chatroom-width !important;
width: $chatroom-width;
.box-flyout {
.chatroom-body {
.chat-area {
min-width: $chatroom-width !important;
width: $chatroom-width;
}
.chatbox-title {
@include make-col(8);
.chatroom-description {
font-size: 80%;
}
}
.chatbox-buttons {
@include make-col(4);
}
.chatroom-body {
.occupants {
.chatroom-features {
.feature {
font-size: $font-size-small;
}
}
}
.chat-area {
min-width: $overlayed-chat-width;
}
}
}
}
}
#conversejs.converse-fullscreen {
.chatroom {
.box-flyout {
.chatbox-title {
@include make-col(9);
}
.chatbox-buttons {
@include make-col(3);
}
}
}
}
@include media-breakpoint-down(sm) {
#conversejs:not(.converse-embedded) {
.chatroom {
width: 100vw !important;
.box-flyout {
.chatbox-navback {
@include make-col(2);
}
.chatbox-title {
@include make-col(7);
}
.chatbox-buttons {
@include make-col(3);
}
}
}
}
}
#conversejs.converse-fullscreen,
#conversejs.converse-mobile {
.chatroom {
.box-flyout {
background-color: $chatroom-head-color;
border: $flyout-padding solid $chatroom-head-color;
border-top: 0.8em solid $chatroom-head-color;
width: 100%;
.chatbox-title {
.chatroom-description {
font-size: 70%;
}
}
.chatroom-body {
@include border-top-radius($chatbox-border-radius);
.chatroom-form-container {
border-radius: $chatbox-border-radius;
}
.chat-area {
border-top-left-radius: $chatbox-border-radius;
.chat-content {
border-top-left-radius: $chatbox-border-radius;
}
&.full {
max-width: 100%;
.new-msgs-indicator {
max-width: 100%;
}
}
}
.occupants {
border-top-right-radius: $chatbox-border-radius;;
padding: $occupants-padding;
.occupants-heading {
font-size: $font-size-large;
}
ul {
&.occupant-list {
li {
font-size: $font-size-small;
}
}
}
}
}
}
.room-invite {
span {
.invited-contact {
margin: 0 0 0.5em -1px;
}
}
}
}

View File

@ -1,4 +1,24 @@
#conversejs {
.oauth-providers {
text-align: center;
.oauth-provider {
margin: 1em 0;
.oauth-login {
margin-left: 0;
color: $link-color;
font-size: $font-size-large;
&:hover {
color: darken($link-color, 20%);
}
i {
color: $link-color;
font-size: $font-size-huge;
margin-right: 0.5em;
}
}
}
}
.set-xmpp-status, .xmpp-status, .roster-contacts {
.fa-circle {
@ -173,15 +193,6 @@
font-weight: bold;
}
.oauth-login {
margin-left: 0;
color: $text-color;
.icon-social:before {
font-size: $font-size-large;
}
}
.controlbox-pane {
.userinfo {
padding-bottom: 1em;
@ -296,6 +307,7 @@
}
.switch-form {
text-align: center;
padding: 2em 0;
p {
margin-top: 0.5em;
@ -357,7 +369,6 @@
.chatbox {
.box-flyout {
top: -100vh;
margin-left: 15px; // Counteracts Bootstrap margins, but
// not clear why needed...
left: 0;
@ -369,10 +380,11 @@
}
#controlbox {
order: 0;
width: 100vw !important;
.box-flyout {
width: 100vw !important;
height: 100vh !important;
margin-left: 30px;
}
.sidebar {
display: block;
@ -393,7 +405,7 @@
}
}
#conversejs:not(.converse-fullscreen) {
#conversejs.converse-overlayed {
#controlbox {
order: -1;
min-width: $controlbox-width !important;
@ -444,3 +456,110 @@
}
}
}
#conversejs.converse-fullscreen,
#conversejs.converse-mobile {
#controlbox {
@include make-col-ready();
@include media-breakpoint-up(md) {
@include make-col(3);
}
@include media-breakpoint-up(lg) {
@include make-col(3);
}
@include media-breakpoint-up(xl) {
@include make-col(2);
}
&.logged-out {
@include make-col(12);
}
margin: 0;
.controlbox-pane {
border-radius: 0;
}
.flyout {
border-radius: 0;
}
#converse-login-panel {
border-radius: 0;
.converse-form {
padding: 3em 2em 3em;
}
}
.toggle-register-login {
line-height: $line-height-huge;
}
.brand-heading-container {
@include make-col(12);
text-align: center;
.brand-heading {
font-size: 150%;
font-size: 600%;
padding: 0.7em 0 0 0;
opacity: 0.8;
color: $blue;
}
.brand-subtitle {
font-size: 90%;
padding: 0.5em;
}
@media screen and (max-width: $mobile-portrait-length) {
.brand-heading {
font-size: 400%;
}
}
}
&.logged-out {
@include make-col(12);
@include fade-in;
width: 100%;
.box-flyout {
width: 100%;
}
}
.box-flyout {
border: 0;
width: 100%;
z-index: 1;
background-color: $controlbox-head-color;
.controlbox-head {
display: none;
}
}
#converse-register, #converse-login {
@include make-col-ready();
@include make-col(8);
@include make-col-offset(2);
@include media-breakpoint-up(sm) {
@include make-col(8);
@include make-col-offset(2);
}
@include media-breakpoint-up(md) {
@include make-col(8);
@include make-col-offset(2);
}
@include media-breakpoint-up(lg) {
@include make-col(6);
@include make-col-offset(3);
}
.title, .instructions {
margin: 1em 0;
}
input[type=submit],
input[type=button] {
width: auto;
}
}
}
}

View File

@ -61,6 +61,23 @@ body.reset {
direction: ltr;
z-index: 1031; // One more than bootstrap navbar
&.converse-overlayed {
> .row {
flex-direction: row-reverse;
}
}
&.converse-fullscreen,
&.converse-mobile {
.converse-chatboxes {
width: 100vw;
right: 15px; // Hack due to padding added by bootstrap
}
}
&.converse-overlayed {
height: 3em;
}
.brand-heading {
font-family: $heading-font;
.icon-conversejs {
@ -76,7 +93,6 @@ body.reset {
z-index: 1031; // One more than bootstrap navbar
position: fixed;
bottom: 0;
height: 2.7rem;
right: 0;
}
@ -143,6 +159,10 @@ body.reset {
font-weight: 700;
}
em {
font-style: italic;
}
ol, ul {
list-style: none;
}
@ -156,10 +176,16 @@ body.reset {
margin: 0;
}
a, a:visited, a:hover, a:not([href]):not([tabindex]) {
a, a:visited, a:not([href]):not([tabindex]) {
text-decoration: none;
color: $link-color;
text-shadow: none;
&:hover {
color: darken($link-color, 20%);
text-decoration: none;
text-shadow: none;
}
&.fa {
color: $subdued-color;
&:hover {

View File

@ -1,5 +1,4 @@
@import "bourbon";
@import "converse/variables";
#conversejs.converse-embedded {
@include box-sizing(border-box);

View File

@ -18,3 +18,20 @@
}
}
#conversejs.converse-fullscreen {
.chatbox.headlines {
.box-flyout {
background-color: $headline-head-color;
}
.chat-head {
&.chat-head-chatbox {
background-color: $headline-head-color;
}
}
.flyout {
border: $flyout-padding solid $headline-head-color;
border-top: 0.8em solid $headline-head-color;
}
}
}

View File

@ -1,4 +1,4 @@
#conversejs:not(.fullscreen) {
#conversejs.converse-overlayed {
#minimized-chats {
order: 100;

View File

@ -32,6 +32,10 @@
.open-chatroom {
&:hover {
background-color: lighten($controlbox-head-color, 45%);
a.add-bookmark,
a.room-info {
display: block !important;
}
}
&.unread-msgs {
.msgs-indicator {
@ -48,7 +52,9 @@
&:hover {
color: $dark-link-color;
}
&.add-bookmark,
&.room-info {
display: none;
&:before {
font-size: 15px;
}

View File

@ -4,10 +4,8 @@
position: relative;
margin: 0;
height: $roster-height;
height: calc(~"100% - #{$controlbox-dropdown-height*2} - 20px");
padding: 0;
overflow: hidden;
// XXX: FIXME
height: calc(100% - 70px);
@ -43,7 +41,6 @@
}
.state-type {
font-size: calc(#{$font-size} - 2px);
height: $controlbox-dropdown-height;
width: 100%;
}
}
@ -142,7 +139,6 @@
.avatar {
float: left;
display: inline-block;
height: $roster-item-height;
}
}
&.current-xmpp-contact span {

View File

@ -63,6 +63,8 @@ $message-them-color: $green !default;
$roster-height: 194px !default;
$flyout-padding: 1.2em;
$chat-head-color: $green !default;
$chat-head-text-color: white !default;
$chat-head-inverse-text-color: white !default;
@ -110,10 +112,11 @@ $font-path: "../fonticons/fonts/" !default;
$normal-font: "Helvetica", "Arial", sans-serif;
$heading-font: 'Century Gothic', futura, 'URW Gothic L', Verdana, sans-serif !default;
$chatroom-head-color: $red !default;
$chatroom-color-light: $light-red !default;
$chatroom-color-dark: $darkest-red !default;
$chatroom-color-light: $light-red !default;
$chatroom-head-color: $red !default;
$chatroom-message-them-color: $green !default;
$chatroom-width: 400px !default;
$headline-head-color: $orange !default;
@ -124,3 +127,30 @@ $box-close-button-padding-right: 4px !default;
$chatbox-button-size: 14px !default;
$fullpage-chatbox-button-size: 16px !default;
$font-size-small: 12px !default;
$font-size: 14px !default;
$font-size-large: 16px !default;
$font-size-huge: 20px !default;
$legend-font-size: 16px !default;
$line-height-small: 14px !default;
$line-height: 16px !default;
$line-height-large: 20px !default;
$line-height-huge: 24px !default;
$occupants-padding: 1em;
$fullpage-chat-head-height: 62px !default;
$fullpage-chat-height: 100vh;
$fullpage-chat-width: 100%;
$fullpage-emoji-picker-height: 150px !default;
$fullpage-max-chat-textarea-height: 400px !default;
$overlayed-chat-head-height: 55px !default;
$overlayed-chat-height: 450px !default;
$overlayed-chat-width: 250px !default;
$overlayed-chatbox-hover-height: 1em !default;
$overlayed-emoji-picker-height: 100px !default;
$overlayed-max-chat-textarea-height: 200px !default;

View File

@ -113,29 +113,41 @@ a:hover, a:focus {
.features-section,
.outro,
.intro {
width: 100%;
padding: 100px 0;
text-align: center;
color: #fff;
width: 100%;
padding: 100px 0;
text-align: center;
color: #fff;
}
.intro {
background: url(images/header.jpg) no-repeat bottom center scroll;
background-color: #211018;
-webkit-background-size: cover;
-moz-background-size: cover;
background-size: cover;
-o-background-size: cover;
background: url(images/header.jpg) no-repeat bottom center scroll;
background-color: #211018;
-webkit-background-size: cover;
-moz-background-size: cover;
background-size: cover;
-o-background-size: cover;
}
.features-section {
background: url('images/bgtr.svg') top right no-repeat, url('images/bgbl.svg') bottom left no-repeat, url('images/bgbl.svg') bottom left no-repeat, url('images/overlay.png'), linear-gradient(45deg, #85505f, #384955, #655361);
background: url('images/bgtr.svg') top right no-repeat, url('images/bgbl.svg') bottom left no-repeat, url('images/bgbl.svg') bottom left no-repeat, url('images/overlay.png'), linear-gradient(45deg, #85505f, #384955, #655361);
}
.features-section a {
color: #82B397;
color: #82B397;
}
.outro {
background: url('images/bgtr.svg') top right no-repeat, url('images/bgbl.svg') bottom left no-repeat, url('images/overlay.png'), linear-gradient(45deg, #384955, #655361, #85505f);
background: url('images/bgtr.svg') top right no-repeat, url('images/bgbl.svg') bottom left no-repeat, url('images/overlay.png'), linear-gradient(45deg, #384955, #655361, #85505f);
}
section {
h2 {
color: #E7A151;
}
h3 {
color: #89B7CD;
}
h4 {
color: #5CBC86;
font-size: 1.5em;
}
}
.brand-heading {
font-family: Futura,Helvetica,Trebuchet MS,Arial,sans-serif;
@ -205,25 +217,38 @@ a:hover, a:focus {
}
.content-section {
padding-top: 100px;
padding-top: 100px;
padding-top: 100px;
min-height: 100vh;
.privacy-policy {
padding-top: 2em;
h4 {
padding-top: 1.5em;
}
p {
font-size: 1.2em;
padding-bottom: 0;
margin-bottom: 1em;
}
}
}
.donate-section {
width: 100%;
padding: 50px 0;
color: #ffffff;
background-color: #211018;
width: 100%;
padding: 50px 0;
color: #ffffff;
background-color: #211018;
}
.donate-section p.bitcoin-header {
margin: 0 0 5px;
margin: 0 0 5px;
}
@media (min-width: 767px) {
.content-section {
padding-top: 150px;
padding-bottom: 50px;
}
.donate-section {
padding: 100px 0;
}
.content-section {
padding-bottom: 50px;
}
.donate-section {
padding: 100px 0;
}
}
.btn {
font-family: Futura,Helvetica,Trebuchet MS,Arial,sans-serif;
@ -332,6 +357,9 @@ ul.features {
clear: both;
font-size: 1.4em;
padding: 2em 0 6em 0;
ul {
padding: 0;
}
}
.sponsors h2 {
text-align: center;

View File

@ -2,13 +2,12 @@
* Converse.js (Web-based XMPP instant messaging client)
* http://conversejs.org
*
* Copyright (c) 2012-2016, JC Brand <jc@opkode.com>
* Copyright (c) 2013-2018, JC Brand <jc@opkode.com>
* Licensed under the Mozilla Public License
*/
@import "font-awesome";
@import "bourbon";
@import "variables";
@import "converse/variables";
@import "bootstrap/scss/functions";
@import "bootstrap/scss/variables";
@ -40,19 +39,16 @@
@import "bootstrap/scss/utilities";
}
@import "core";
@import "converse/core";
@import "forms";
@import "profile";
@import "chatbox";
@import "converse/chatbox";
@import "controlbox";
@import "roomslist";
@import "roster";
@import "chatrooms";
@import "converse/chatrooms";
@import "headline";
@import "messages";
@import "converse/minimized_chats";
@import "minimized_chats";
@import "bookmarks";
@import "awesomplete";
@import "embedded";

View File

@ -1,71 +0,0 @@
#converse-embedded-chat,
#conversejs:not(.fullscreen) {
.chat-head {
border-top-left-radius: $chatbox-border-radius;
border-top-right-radius: $chatbox-border-radius;
@media screen and (max-height: $mobile-landscape-height) {
border-top-left-radius: 0;
border-top-right-radius: 0;
}
@media screen and (max-width: $mobile-portrait-length) {
border-top-left-radius: 0;
border-top-right-radius: 0;
}
}
.chatbox {
min-width: $chat-width!important;
width: $chat-width;
.box-flyout {
min-width: $chat-width!important;
width: $chat-width;
}
.chat-body {
.chat-message {
line-height: $line-height-large;
.chat-msg-author {
line-height: $line-height-large;
}
.chat-msg-content {
line-height: $line-height-large;
.emojione {
margin-bottom: -5px;
}
}
}
}
}
.chatbox {
form.sendXMPPMessage {
.chat-toolbar {
li {
.toolbar-menu {
min-width: 235px;
ul {
&.emoji-toolbar {
width: 100%;
.emoji-category {
float: left;
}
}
}
}
}
}
}
}
}
@include media-breakpoint-down(sm) {
#conversejs:not(.converse-fullscreen):not(.converse-embedded) {
> .row {
flex-direction: column;
&.no-gutters {
margin: -1em;
}
}
}
}

View File

@ -1,22 +0,0 @@
#conversejs {
.chatbox {
&.chatroom {
min-width: $chatroom-width !important;
width: $chatroom-width;
.box-flyout {
min-width: $chatroom-width !important;
width: $chatroom-width;
}
.chatroom-body {
.occupants {
.chatroom-features {
.feature {
font-size: $font-size-small;
}
}
}
}
}
}
}

View File

@ -1,5 +0,0 @@
#conversejs {
> .row {
flex-direction: row-reverse;
}
}

View File

@ -1,26 +0,0 @@
$max-chat-textarea-height: 200px !default;
$emoji-picker-height: 100px !default;
$roster-item-height: 60px !default;
$chat-head-height: 55px !default;
$controlbox-dropdown-height: 25px !default;
$controlbox-head-height: 55px !default;
$chatbox-hover-height: 1em !default;
$font-size-small: 12px !default;
$font-size: 14px !default;
$font-size-large: 16px !default;
$font-size-huge: 20px !default;
$legend-font-size: 16px !default;
$line-height-small: 14px !default;
$line-height: 16px !default;
$line-height-large: 20px !default;
$chat-width: 250px !default;
$chat-height: 450px !default;
$chatroom-width: 400px !default;

View File

@ -1,58 +0,0 @@
/*!
* Converse.js (Web-based XMPP instant messaging client)
* http://conversejs.org
*
* Copyright (c) 2012-2014, JC Brand <jc@opkode.com>
* Licensed under the Mozilla Public License
*/
@import "font-awesome";
@import "bourbon";
@import "variables";
@import "inverse/variables";
@import "bootstrap/scss/functions";
@import "bootstrap/scss/variables";
@import "bootstrap/scss/mixins";
#conversejs {
@import "bootstrap/scss/root";
@import "bootstrap/scss/reboot";
@import "bootstrap/scss/type";
@import "bootstrap/scss/images";
@import "bootstrap/scss/grid";
@import "bootstrap/scss/forms";
@import "bootstrap/scss/buttons";
@import "bootstrap/scss/transitions";
@import "bootstrap/scss/dropdown";
@import "bootstrap/scss/button-group";
@import "bootstrap/scss/input-group";
@import "bootstrap/scss/custom-forms";
@import "bootstrap/scss/card";
@import "bootstrap/scss/breadcrumb";
@import "bootstrap/scss/badge";
@import "bootstrap/scss/alert";
@import "bootstrap/scss/media";
@import "bootstrap/scss/list-group";
@import "bootstrap/scss/close";
@import "bootstrap/scss/modal";
@import "bootstrap/scss/tooltip";
@import "bootstrap/scss/popover";
@import "bootstrap/scss/utilities";
}
@import "core";
@import "inverse/core";
@import "forms";
@import "profile";
@import "chatbox";
@import "inverse/chatbox";
@import "controlbox";
@import "inverse/controlbox";
@import "roster";
@import "roomslist";
@import "inverse/roster";
@import "bookmarks";
@import "chatrooms";
@import "inverse/chatrooms";
@import "headline";
@import "inverse/headline";
@import "messages";
@import "awesomplete"

View File

@ -1,96 +0,0 @@
#conversejs.fullscreen {
.chatbox-btn {
font-size: $font-size-large;
margin: 0 0.3em;
}
.flyout {
border-radius: 0;
border: $flyout-padding solid $chat-head-color;
border-top: 0.8em solid $chat-head-color;
bottom: 0;
}
.chat-head {
font-size: 20px;
padding: 0;
.user-custom-message {
font-size: 50%;
height: auto;
line-height: $line-height;
}
}
.chatbox {
width: 100%;
height: 100%;
margin: 0;
@include make-col-ready();
@include media-breakpoint-up(md) {
@include make-col(9);
}
@include media-breakpoint-up(xl) {
@include make-col(10);
}
.box-flyout {
background-color: $chat-head-color;
height: 100vh;
width: 100%;
box-shadow: none;
}
.chat-body {
background-color: $chat-head-color;
border-top-left-radius: $chatbox-border-radius;
border-top-right-radius: $chatbox-border-radius;
.chat-message {
line-height: $line-height;
font-size: $font-size-small;
.chat-msg-author {
line-height: $line-height;
}
.chat-msg-content {
line-height: $line-height;
.emojione {
height: $line-height;
margin-bottom: -$line-height/4;
}
}
}
}
.chat-content {
border-top-left-radius: $chatbox-border-radius;
border-top-right-radius: $chatbox-border-radius;
}
.chat-title {
font-size: $font-size-huge;
line-height: $line-height-huge;
}
.sendXMPPMessage {
ul {
width: 100%;
}
.toggle-smiley {
ul {
&.emoji-toolbar {
.emoji-category-picker {
margin-right: 5em;
}
.emoji-category {
padding-left: 10px;
padding-right: 10px;
}
}
}
}
}
}
}
@media screen and (max-width: 767px) {
#conversejs:not(.converse-embedded).fullscreen {
.chatbox {
width: calc(100% - 50px);
}
}
}

View File

@ -1,64 +0,0 @@
#conversejs.converse-fullscreen,
#conversejs.converse-mobile {
.chat-head-chatroom {
height: $chatroom-head-height;
font-size: 20px;
.chat-title {
.chatroom-description {
font-size: 65%;
}
}
}
.chatroom {
.box-flyout {
background-color: $chatroom-head-color;
border: $flyout-padding solid $chatroom-head-color;
border-top: 0.8em solid $chatroom-head-color;
width: 100%;
.chatroom-body {
@include border-top-radius($chatbox-border-radius);
.chatroom-form-container {
border-radius: $chatbox-border-radius;
}
.chat-area {
border-top-left-radius: $chatbox-border-radius;
min-width: auto;
.chat-content {
border-top-left-radius: $chatbox-border-radius;
}
&.full {
max-width: 100%;
.new-msgs-indicator {
max-width: 100%;
}
}
}
.occupants {
border-top-right-radius: $chatbox-border-radius;;
padding: $padding;
.occupants-heading {
font-size: $font-size-large;
}
ul {
&.occupant-list {
li {
font-size: $font-size-small;
}
}
}
}
}
}
.room-invite {
span {
.invited-contact {
margin: 0 0 0.5em -1px;
}
}
}
}
}

View File

@ -1,102 +0,0 @@
#conversejs.fullscreen {
#controlbox {
@include make-col-ready();
@include media-breakpoint-up(md) {
@include make-col(3);
}
@include media-breakpoint-up(xl) {
@include make-col(2);
}
&.logged-out {
@include make-col(12);
}
margin: 0;
.controlbox-pane {
border-radius: 0;
}
.flyout {
border-radius: 0;
}
#converse-login-panel {
border-radius: 0;
.converse-form {
padding: 3em 2em 3em;
}
}
.toggle-register-login {
line-height: $line-height-huge;
}
.brand-heading-container {
@include make-col(12);
text-align: center;
.brand-heading {
font-size: 150%;
font-size: 600%;
padding: 0.7em 0 0 0;
opacity: 0.8;
color: $blue;
}
.brand-subtitle {
font-size: 90%;
padding: 0.5em;
}
@media screen and (max-width: $mobile-portrait-length) {
.brand-heading {
font-size: 400%;
}
}
}
&.logged-out {
@include make-col(12);
@include fade-in;
width: 100%;
.box-flyout {
width: 100%;
}
}
.box-flyout {
border: 0;
width: 100%;
z-index: 1;
background-color: $controlbox-head-color;
.controlbox-head {
display: none;
}
}
#converse-register, #converse-login {
@include make-col-ready();
@include make-col(8);
@include make-col-offset(2);
@include media-breakpoint-up(sm) {
@include make-col(8);
@include make-col-offset(2);
}
@include media-breakpoint-up(md) {
@include make-col(8);
@include make-col-offset(2);
}
@include media-breakpoint-up(lg) {
@include make-col(6);
@include make-col-offset(3);
}
.title, .instructions {
margin: 1em 0;
}
input[type=submit],
input[type=button] {
width: auto;
}
}
}
}

View File

@ -1,42 +0,0 @@
body {
font-family: "Lora", "Helvetica Neue", Helvetica, Arial, sans-serif;
color: #ffffff;
background-color: $global-background-color;
.brand-heading {
font-size: 600%;
margin-left: -10%;
&.fade-in {
@include fade-in;
@include animation-delay(2s);
}
.icon-conversejs {
font-size: 88%;
}
}
div.content {
height: 100vh;
width: 100vw;
position: fixed;
background-color: $global-background-color;
.inner-content {
text-align: center;
padding: 7%;
@include calc(padding-left, '5% + #{$controlbox-width}');
p.no-chats {
padding-right: 10%;
font-size: 120%;
}
}
}
}
#conversejs.fullscreen {
.converse-chatboxes {
width: 100vw;
right: 15px; // Hack due to padding added by bootstrap
}
}

View File

@ -1,17 +0,0 @@
#conversejs.fullscreen {
.chatbox.headlines {
.box-flyout {
background-color: $headline-head-color;
}
.chat-head {
&.chat-head-chatbox {
background-color: $headline-head-color;
}
}
.flyout {
border: $flyout-padding solid $headline-head-color;
border-top: 0.8em solid $headline-head-color;
}
}
}

View File

@ -1,58 +0,0 @@
#conversejs.fullscreen {
#minimized-chats {
border-top-left-radius: $chatbox-border-radius;
border-top-right-radius: $chatbox-border-radius;
color: $inverse-link-color;
display: none;
float: right;
font-weight: bold;
height: 100%;
margin: 0 $chat-gutter;
padding: 0;
width: 130px;
#toggle-minimized-chats {
border-top-left-radius: $chatbox-border-radius;
border-top-right-radius: $chatbox-border-radius;
background-color: $link-color;
color: white;
position: relative;
padding: 10px 0 0 0;
display: block;
width: 100%;
height: 100%;
text-align: center;
}
.minimized-chats-flyout {
height: auto;
bottom: $bottom-gutter-height;
.chat-head {
padding: 0.3em;
border-radius: $chatbox-border-radius;
width: $minimized-chats-width;
height: 35px;
margin-bottom: 0.2em;
box-shadow: 1px 3px 5px 3px rgba(0, 0, 0, 0.4);
}
&.minimized {
height: auto;
}
}
.unread-message-count {
font-weight: bold;
background-color: white;
border: 1px solid;
text-shadow: 1px 1px 0 $text-shadow-color;
color: $warning-color;
border-radius: 5px;
padding: 2px 4px;
font-size: 16px;
text-align: center;
position: absolute;
right: 116px;
bottom: 10px;
}
}
}

View File

@ -1,5 +0,0 @@
#conversejs {
#converse-roster {
padding-bottom: 3em;
}
}

View File

@ -1,34 +0,0 @@
$max-chat-textarea-height: 400px !default;
$emoji-picker-height: 150px !default;
$roster-item-height: 30px !default;
$flyout-padding: 1.2em;
$chat-head-height: 62px !default;
$controlbox-dropdown-height: 30px !default;
$controlbox-head-height: 63px !default;
$rounded-border-radius: 4px !default;
$chatbox-hover-height: 6px !default;
$font-size-small: 14px !default;
$font-size: 16px !default;
$font-size-large: 18px !default;
$font-size-huge: 26px !default;
$legend-font-size: 18px !default;
$line-height-small: 20px !default;
$line-height: 22px !default;
$line-height-large: 24px !default;
$line-height-huge: 30px !default;
$chat-width: 100% !default;
$chat-height: 100%;
$padding: 1em;
$chatroom-head-height: 62px !default;
$chatroom-width: 300px !default;

View File

@ -4,12 +4,10 @@
define([
"jasmine",
"jquery",
"converse-core",
"utils",
"mock",
"test-utils"
], factory);
} (this, function (jasmine, $, converse, utils, mock, test_utils) {
} (this, function (jasmine, $, mock, test_utils) {
"use strict";
var $iq = converse.env.$iq,
$msg = converse.env.$msg,
@ -381,14 +379,14 @@
['http://jabber.org/protocol/pubsub#publish-options']
).then(function () {
/* Client requests all items
* -------------------------
*
* <iq from='juliet@capulet.lit/randomID' type='get' id='retrieve1'>
* <pubsub xmlns='http://jabber.org/protocol/pubsub'>
* <items node='storage:bookmarks'/>
* </pubsub>
* </iq>
*/
* -------------------------
*
* <iq from='juliet@capulet.lit/randomID' type='get' id='retrieve1'>
* <pubsub xmlns='http://jabber.org/protocol/pubsub'>
* <items node='storage:bookmarks'/>
* </pubsub>
* </iq>
*/
var IQ_id;
expect(_.filter(_converse.connection.send.calls.all(), function (call) {
var stanza = call.args[0];
@ -411,27 +409,29 @@
}).length).toBe(1);
/*
* Server returns all items
* ------------------------
* <iq type='result'
* to='juliet@capulet.lit/randomID'
* id='retrieve1'>
* <pubsub xmlns='http://jabber.org/protocol/pubsub'>
* <items node='storage:bookmarks'>
* <item id='current'>
* <storage xmlns='storage:bookmarks'>
* <conference name='The Play&apos;s the Thing'
* autojoin='true'
* jid='theplay@conference.shakespeare.lit'>
* <nick>JC</nick>
* </conference>
* </storage>
* </item>
* </items>
* </pubsub>
* </iq>
*/
* Server returns all items
* ------------------------
* <iq type='result'
* to='juliet@capulet.lit/randomID'
* id='retrieve1'>
* <pubsub xmlns='http://jabber.org/protocol/pubsub'>
* <items node='storage:bookmarks'>
* <item id='current'>
* <storage xmlns='storage:bookmarks'>
* <conference name='The Play&apos;s the Thing'
* autojoin='true'
* jid='theplay@conference.shakespeare.lit'>
* <nick>JC</nick>
* </conference>
* </storage>
* </item>
* </items>
* </pubsub>
* </iq>
*/
expect(_converse.bookmarks.models.length).toBe(0);
spyOn(_converse.bookmarks, 'onBookmarksReceived').and.callThrough();
var stanza = $iq({'to': _converse.connection.jid, 'type':'result', 'id':IQ_id})
.c('pubsub', {'xmlns': Strophe.NS.PUBSUB})
.c('items', {'node': 'storage:bookmarks'})
@ -448,11 +448,13 @@
'jid': 'another@conference.shakespeare.lit'
}); // Purposefully exclude the <nick> element to test #1043
_converse.connection._dataRecv(test_utils.createRequest(stanza));
return test_utils.waitUntil(() => _converse.bookmarks.onBookmarksReceived.calls.count(), 300)
}).then(() => {
expect(_converse.bookmarks.models.length).toBe(2);
expect(_converse.bookmarks.findWhere({'jid': 'theplay@conference.shakespeare.lit'}).get('autojoin')).toBe(true);
expect(_converse.bookmarks.findWhere({'jid': 'another@conference.shakespeare.lit'}).get('autojoin')).toBe(false);
done();
});
}).catch(_.partial(console.error, _));
}));
describe("The rooms panel", function () {

View File

@ -2,12 +2,10 @@
define([
"jquery",
"jasmine",
"utils",
"converse-core",
"mock",
"test-utils"
], factory);
} (this, function ($, jasmine, utils, converse, mock, test_utils) {
} (this, function ($, jasmine, mock, test_utils) {
"use strict";
var _ = converse.env._;
var $iq = converse.env.$iq;
@ -429,9 +427,9 @@
var view = _converse.chatboxviews.get(contact_jid);
expect(chatbox).toBeDefined();
expect(view).toBeDefined();
var $toolbar = $(view.el).find('ul.chat-toolbar');
expect($toolbar.length).toBe(1);
expect($toolbar.children('li').length).toBe(1);
var toolbar = view.el.querySelector('ul.chat-toolbar');
expect(_.isElement(toolbar)).toBe(true);
expect(toolbar.querySelectorAll(':scope > li').length).toBe(1);
done();
}));
@ -458,7 +456,7 @@
var timeout = false;
test_utils.waitUntil(function () {
return utils.isVisible(view.el.querySelector('.toggle-smiley .emoji-picker-container'));
return u.isVisible(view.el.querySelector('.toggle-smiley .emoji-picker-container'));
}, 500).then(function () {
var picker = view.el.querySelector('.toggle-smiley .emoji-picker-container');
var items = picker.querySelectorAll('.emoji-picker li');
@ -747,7 +745,7 @@
expect(chatbox.messages.length).toEqual(1);
var msg_obj = chatbox.messages.models[0];
expect(msg_obj.get('sender')).toEqual('me');
expect(msg_obj.get('delayed')).toEqual(false);
expect(msg_obj.get('is_delayed')).toEqual(false);
var $chat_content = $(chatboxview.el).find('.chat-content');
var status_text = $chat_content.find('.chat-info.chat-state-notification').text();
expect(status_text).toBe('Typing from another device');
@ -895,7 +893,7 @@
expect(chatbox.messages.length).toEqual(1);
var msg_obj = chatbox.messages.models[0];
expect(msg_obj.get('sender')).toEqual('me');
expect(msg_obj.get('delayed')).toEqual(false);
expect(msg_obj.get('is_delayed')).toEqual(false);
var $chat_content = $(chatboxview.el).find('.chat-content');
var status_text = $chat_content.find('.chat-info.chat-state-notification').text();
expect(status_text).toBe('Stopped typing on the other device');

View File

@ -1,6 +1,6 @@
(function (root, factory) {
define(["jquery", "jasmine", "mock", "converse-core", "test-utils", "utils" ], factory);
} (this, function ($, jasmine, mock, converse, test_utils, utils) {
define(["jquery", "jasmine", "mock", "test-utils" ], factory);
} (this, function ($, jasmine, mock, test_utils) {
var _ = converse.env._;
var $pres = converse.env.$pres;
var $iq = converse.env.$iq;
@ -657,6 +657,7 @@
expect(indicator.getAttribute('data-isodate')).toEqual(moment().startOf('day').format());
expect(indicator.querySelector('time').getAttribute('class')).toEqual('separator-text');
expect(indicator.querySelector('time').textContent).toEqual(moment().startOf('day').format("dddd MMM Do YYYY"));
expect(chat_content.querySelectorAll('div.chat-info').length).toBe(2);
expect(chat_content.querySelector('div.chat-info:last-child').textContent).toBe(
"some1 has entered the room"
);
@ -672,9 +673,9 @@
.c('status', 'Disconnected: Replaced by new connection').up()
.c('x', {xmlns: Strophe.NS.MUC_USER})
.c('item', {
'affiliation': 'none',
'affiliation': 'owner',
'jid': 'some1@localhost/_converse.js-290929789',
'role': 'none'
'role': 'moderator'
});
_converse.connection._dataRecv(test_utils.createRequest(presence));
@ -685,7 +686,7 @@
expect(indicator.getAttribute('data-isodate')).toEqual(moment().startOf('day').format());
expect(indicator.querySelector('time').textContent).toEqual(moment().startOf('day').format("dddd MMM Do YYYY"));
expect($(chat_content).find('div.chat-info').length).toBe(4);
expect(chat_content.querySelectorAll('div.chat-info').length).toBe(3);
expect($(chat_content).find('div.chat-info:last').html()).toBe(
'some1 has left the room. '+
'"Disconnected: Replaced by new connection"');
@ -713,14 +714,14 @@
});
_converse.connection._dataRecv(test_utils.createRequest(presence));
var $time = $chat_content.find('time');
expect($time.length).toEqual(4);
let time = chat_content.querySelectorAll('time.separator-text');
expect(time.length).toEqual(4);
var $indicator = $chat_content.find('.date-separator:eq(3)');
expect($indicator.attr('class')).toEqual('message date-separator');
expect($indicator.data('isodate')).toEqual(moment().startOf('day').format());
expect($indicator.find('time').text()).toEqual(moment().startOf('day').format("dddd MMM Do YYYY"));
expect($chat_content.find('div.chat-info').length).toBe(5);
expect(chat_content.querySelectorAll('div.chat-info').length).toBe(4);
expect($chat_content.find('div.chat-info:last').html()).toBe("newguy has entered the room");
jasmine.clock().tick(ONE_DAY_LATER);
@ -752,15 +753,15 @@
});
_converse.connection._dataRecv(test_utils.createRequest(presence));
$time = $chat_content.find('time');
expect($time.length).toEqual(6);
time = chat_content.querySelectorAll('time.separator-text');
expect(time.length).toEqual(6);
$indicator = $chat_content.find('.date-separator:eq(5)');
expect($indicator.attr('class')).toEqual('message date-separator');
expect($indicator.data('isodate')).toEqual(moment().startOf('day').format());
expect($indicator.find('time').text()).toEqual(moment().startOf('day').format("dddd MMM Do YYYY"));
expect($chat_content.find('div.chat-info').length).toBe(6);
expect(chat_content.querySelectorAll('div.chat-info').length).toBe(5);
expect($chat_content.find('div.chat-info:last').html()).toBe(
'newguy has left the room. '+
'"Disconnected: Replaced by new connection"');
@ -813,9 +814,8 @@
.c('field', {'type':'text-single', 'var':'muc#roominfo_occupants', 'label':'Number of occupants'})
.c('value').t(0);
_converse.connection._dataRecv(test_utils.createRequest(features_stanza));
test_utils.waitUntil(function () {
return _.get(view.el.querySelector('.chatroom-description'), 'textContent');
}).then(function () {
test_utils.waitUntil(() => _.get(view.el.querySelector('.chatroom-description'), 'textContent'))
.then(function () {
expect($(view.el.querySelector('.chatroom-description')).text()).toBe('This is the description');
done();
});
@ -1113,7 +1113,7 @@
}).catch(_.partial(_converse.log, _, Strophe.LogLevel.FATAL));
}));
it("shows users currently present in the room",
it("shows all members even if they're not currently present in the room",
mock.initConverseWithPromises(
null, ['rosterGroupsFetched'], {},
function (done, _converse) {
@ -1145,6 +1145,61 @@
expect(occupants.querySelectorAll('li .occupant-nick')[index].textContent.trim()).toBe(mock.chatroom_names[i]);
}
// Test users leaving the room
// http://xmpp.org/extensions/xep-0045.html#exit
for (i=mock.chatroom_names.length-1; i>-1; i--) {
name = mock.chatroom_names[i];
role = mock.chatroom_roles[name].role;
// See example 21 http://xmpp.org/extensions/xep-0045.html#enter-pres
presence = $pres({
to:'dummy@localhost/pda',
from:'lounge@localhost/'+name,
type: 'unavailable'
}).c('x').attrs({xmlns:'http://jabber.org/protocol/muc#user'})
.c('item').attrs({
affiliation: mock.chatroom_roles[name].affiliation,
jid: name.replace(/ /g,'.').toLowerCase() + '@localhost',
role: 'none'
}).nodeTree;
_converse.connection._dataRecv(test_utils.createRequest(presence));
expect(occupants.querySelectorAll('li').length).toBe(7);
}
done();
}).catch(_.partial(console.error, _));
}));
it("shows users currently present in the room",
mock.initConverseWithPromises(
null, ['rosterGroupsFetched'], {},
function (done, _converse) {
test_utils.openAndEnterChatRoom(_converse, 'lounge', 'localhost', 'dummy').then(function() {
var name;
var view = _converse.chatboxviews.get('lounge@localhost'),
occupants = view.el.querySelector('.occupant-list');
var presence, role, jid, model;
for (var i=0; i<mock.chatroom_names.length; i++) {
name = mock.chatroom_names[i];
role = mock.chatroom_roles[name].role;
// See example 21 http://xmpp.org/extensions/xep-0045.html#enter-pres
jid =
presence = $pres({
to:'dummy@localhost/pda',
from:'lounge@localhost/'+name
}).c('x').attrs({xmlns:'http://jabber.org/protocol/muc#user'})
.c('item').attrs({
affiliation: 'none',
jid: name.replace(/ /g,'.').toLowerCase() + '@localhost',
role: role
}).up()
.c('status').attrs({code:'110'}).nodeTree;
_converse.connection._dataRecv(test_utils.createRequest(presence));
expect(occupants.querySelectorAll('li').length).toBe(2+i);
model = view.occupantsview.model.where({'nick': name})[0];
var index = view.occupantsview.model.indexOf(model);
expect(occupants.querySelectorAll('li .occupant-nick')[index].textContent.trim()).toBe(mock.chatroom_names[i]);
}
// Test users leaving the room
// http://xmpp.org/extensions/xep-0045.html#exit
for (i=mock.chatroom_names.length-1; i>-1; i--) {
@ -1710,7 +1765,11 @@
.c('status').attrs({code:'110'}).nodeTree;
_converse.connection._dataRecv(test_utils.createRequest(presence));
expect($chat_content.find('div.chat-info').length).toBe(2);
// XXX: currently we still have an additional "has entered the room"
// notification for the new nickname. Ideally we'd not have
// that, but that's probably not possible without some
// significant refactoring.
expect($chat_content.find('div.chat-info').length).toBe(3);
expect($chat_content.find('div.chat-info').get(1).textContent).toBe(
__(_converse.muc.new_nickname_messages["303"], "newnick")
);
@ -1741,6 +1800,8 @@
"<query xmlns='http://jabber.org/protocol/disco#info'/>"+
"</iq>");
var view = _converse.chatboxviews.get('coven@chat.shakespeare.lit');
spyOn(view.model, 'parseRoomFeatures').and.callThrough();
/* <iq from='coven@chat.shakespeare.lit'
* id='ik3vs715'
* to='hag66@shakespeare.lit/pda'
@ -1780,16 +1841,17 @@
.c('feature', {'var': 'muc_unmoderated'}).up()
.c('feature', {'var': 'muc_nonanonymous'});
_converse.connection._dataRecv(test_utils.createRequest(features_stanza));
var view = _converse.chatboxviews.get('coven@chat.shakespeare.lit');
expect(view.model.get('features_fetched')).toBe(true);
expect(view.model.get('passwordprotected')).toBe(true);
expect(view.model.get('hidden')).toBe(true);
expect(view.model.get('temporary')).toBe(true);
expect(view.model.get('open')).toBe(true);
expect(view.model.get('unmoderated')).toBe(true);
expect(view.model.get('nonanonymous')).toBe(true);
done();
test_utils.waitUntil(() => view.model.parseRoomFeatures.calls.count(), 300)
.then(() => {
expect(view.model.get('features_fetched')).toBeTruthy();
expect(view.model.get('passwordprotected')).toBe(true);
expect(view.model.get('hidden')).toBe(true);
expect(view.model.get('temporary')).toBe(true);
expect(view.model.get('open')).toBe(true);
expect(view.model.get('unmoderated')).toBe(true);
expect(view.model.get('nonanonymous')).toBe(true);
done();
});
}));
it("updates the shown features when the room configuration has changed",
@ -2888,6 +2950,7 @@
var muc_iq;
var sent_IQs = [], IQ_ids = [];
var invitee_jid, sent_stanza, sent_id;
var sendIQ = _converse.connection.sendIQ;
spyOn(_converse.connection, 'sendIQ').and.callFake(function (iq, callback, errback) {
sent_IQs.push(iq);
@ -2896,6 +2959,9 @@
_converse.api.rooms.open('coven@chat.shakespeare.lit', {'nick': 'dummy'});
var view = _converse.chatboxviews.get('coven@chat.shakespeare.lit');
spyOn(view.model, 'parseRoomFeatures').and.callThrough();
// State that the chat is members-only via the features IQ
var features_stanza = $iq({
from: 'coven@chat.shakespeare.lit',
@ -2915,111 +2981,102 @@
.c('feature', {'var': 'muc_membersonly'}).up();
_converse.connection._dataRecv(test_utils.createRequest(features_stanza));
var view = _converse.chatboxviews.get('coven@chat.shakespeare.lit');
expect(view.model.get('membersonly')).toBeTruthy();
test_utils.waitUntil(() => view.model.parseRoomFeatures.calls.count(), 300)
.then(() => {
expect(view.model.get('membersonly')).toBeTruthy();
test_utils.createContacts(_converse, 'current');
test_utils.createContacts(_converse, 'current');
var sent_stanza, sent_id;
spyOn(_converse.connection, 'send').and.callFake(function (stanza) {
if (stanza.nodeTree && stanza.nodeTree.nodeName === 'message') {
sent_id = stanza.nodeTree.getAttribute('id');
sent_stanza = stanza;
}
});
var name = mock.cur_names[0];
var invitee_jid = name.replace(/ /g,'.').toLowerCase() + '@localhost';
var reason = "Please join this chat room";
view.model.directInvite(invitee_jid, reason);
spyOn(_converse.connection, 'send').and.callFake(function (stanza) {
if (stanza.nodeTree && stanza.nodeTree.nodeName === 'message') {
sent_id = stanza.nodeTree.getAttribute('id');
sent_stanza = stanza;
}
});
var name = mock.cur_names[0];
invitee_jid = name.replace(/ /g,'.').toLowerCase() + '@localhost';
var reason = "Please join this chat room";
view.model.directInvite(invitee_jid, reason);
// Check in reverse order that we requested all three lists
// (member, owner and admin).
var admin_iq_id = IQ_ids.pop();
var owner_iq_id = IQ_ids.pop();
var member_iq_id = IQ_ids.pop();
// Check in reverse order that we requested all three lists
// (member, owner and admin).
var admin_iq_id = IQ_ids.pop();
var owner_iq_id = IQ_ids.pop();
var member_iq_id = IQ_ids.pop();
expect(sent_IQs.pop().toLocaleString()).toBe(
"<iq to='coven@chat.shakespeare.lit' type='get' xmlns='jabber:client' id='"+admin_iq_id+"'>"+
"<query xmlns='http://jabber.org/protocol/muc#admin'>"+
"<item affiliation='admin'/>"+
"</query>"+
"</iq>");
expect(sent_IQs.pop().toLocaleString()).toBe(
"<iq to='coven@chat.shakespeare.lit' type='get' xmlns='jabber:client' id='"+owner_iq_id+"'>"+
"<query xmlns='http://jabber.org/protocol/muc#admin'>"+
"<item affiliation='owner'/>"+
"</query>"+
"</iq>");
expect(sent_IQs.pop().toLocaleString()).toBe(
"<iq to='coven@chat.shakespeare.lit' type='get' xmlns='jabber:client' id='"+member_iq_id+"'>"+
"<query xmlns='http://jabber.org/protocol/muc#admin'>"+
"<item affiliation='member'/>"+
"</query>"+
"</iq>");
expect(sent_IQs.pop().toLocaleString()).toBe(
"<iq to='coven@chat.shakespeare.lit' type='get' xmlns='jabber:client' id='"+admin_iq_id+"'>"+
"<query xmlns='http://jabber.org/protocol/muc#admin'>"+
"<item affiliation='admin'/>"+
"</query>"+
"</iq>");
expect(sent_IQs.pop().toLocaleString()).toBe(
"<iq to='coven@chat.shakespeare.lit' type='get' xmlns='jabber:client' id='"+owner_iq_id+"'>"+
"<query xmlns='http://jabber.org/protocol/muc#admin'>"+
"<item affiliation='owner'/>"+
"</query>"+
"</iq>");
expect(sent_IQs.pop().toLocaleString()).toBe(
"<iq to='coven@chat.shakespeare.lit' type='get' xmlns='jabber:client' id='"+member_iq_id+"'>"+
"<query xmlns='http://jabber.org/protocol/muc#admin'>"+
"<item affiliation='member'/>"+
"</query>"+
"</iq>");
/* Now the service sends the member list to the user
*
* <iq from='coven@chat.shakespeare.lit'
* id='member3'
* to='crone1@shakespeare.lit/desktop'
* type='result'>
* <query xmlns='http://jabber.org/protocol/muc#admin'>
* <item affiliation='member'
* jid='hag66@shakespeare.lit'
* nick='thirdwitch'
* role='participant'/>
* </query>
* </iq>
*/
var member_list_stanza = $iq({
'from': 'coven@chat.shakespeare.lit',
'id': member_iq_id,
'to': 'dummy@localhost/resource',
'type': 'result'
}).c('query', {'xmlns': Strophe.NS.MUC_ADMIN})
.c('item', {
'affiliation': 'member',
'jid': 'hag66@shakespeare.lit',
'nick': 'thirdwitch',
'role': 'participant'
});
_converse.connection._dataRecv(test_utils.createRequest(member_list_stanza));
/* Now the service sends the member list to the user
*
* <iq from='coven@chat.shakespeare.lit'
* id='member3'
* to='crone1@shakespeare.lit/desktop'
* type='result'>
* <query xmlns='http://jabber.org/protocol/muc#admin'>
* <item affiliation='member'
* jid='hag66@shakespeare.lit'
* nick='thirdwitch'
* role='participant'/>
* </query>
* </iq>
*/
var member_list_stanza = $iq({
'from': 'coven@chat.shakespeare.lit',
'id': member_iq_id,
'to': 'dummy@localhost/resource',
'type': 'result'
}).c('query', {'xmlns': Strophe.NS.MUC_ADMIN})
.c('item', {
'affiliation': 'member',
'jid': 'hag66@shakespeare.lit',
'nick': 'thirdwitch',
'role': 'participant'
});
_converse.connection._dataRecv(test_utils.createRequest(member_list_stanza));
var admin_list_stanza = $iq({
'from': 'coven@chat.shakespeare.lit',
'id': admin_iq_id,
'to': 'dummy@localhost/resource',
'type': 'result'
}).c('query', {'xmlns': Strophe.NS.MUC_ADMIN})
.c('item', {
'affiliation': 'admin',
'jid': 'wiccarocks@shakespeare.lit',
'nick': 'secondwitch'
});
_converse.connection._dataRecv(test_utils.createRequest(admin_list_stanza));
var admin_list_stanza = $iq({
'from': 'coven@chat.shakespeare.lit',
'id': admin_iq_id,
'to': 'dummy@localhost/resource',
'type': 'result'
}).c('query', {'xmlns': Strophe.NS.MUC_ADMIN})
.c('item', {
'affiliation': 'admin',
'jid': 'wiccarocks@shakespeare.lit',
'nick': 'secondwitch'
});
_converse.connection._dataRecv(test_utils.createRequest(admin_list_stanza));
var owner_list_stanza = $iq({
'from': 'coven@chat.shakespeare.lit',
'id': owner_iq_id,
'to': 'dummy@localhost/resource',
'type': 'result'
}).c('query', {'xmlns': Strophe.NS.MUC_ADMIN})
.c('item', {
'affiliation': 'owner',
'jid': 'crone1@shakespeare.lit',
});
_converse.connection._dataRecv(test_utils.createRequest(owner_list_stanza));
test_utils.waitUntil(() => {
return _.filter(
_converse.connection.IQ_stanzas,
(iq) => {
if (iq.nodeTree.getAttribute('to') === 'coven@chat.shakespeare.lit' && iq.nodeTree.getAttribute('type') === 'set') {
muc_iq = iq;
return true;
}
return false;
}).length;
}).then(function () {
var owner_list_stanza = $iq({
'from': 'coven@chat.shakespeare.lit',
'id': owner_iq_id,
'to': 'dummy@localhost/resource',
'type': 'result'
}).c('query', {'xmlns': Strophe.NS.MUC_ADMIN})
.c('item', {
'affiliation': 'owner',
'jid': 'crone1@shakespeare.lit',
});
_converse.connection._dataRecv(test_utils.createRequest(owner_list_stanza));
return test_utils.waitUntil(() => IQ_ids.length, 300);
}).then(() => {
// Check that the member list now gets updated
expect(muc_iq.toLocaleString()).toBe("<iq to='coven@chat.shakespeare.lit' type='set' xmlns='jabber:client' id='"+muc_iq.nodeTree.getAttribute('id')+"'>"+
"<query xmlns='http://jabber.org/protocol/muc#admin'>"+
@ -3112,7 +3169,7 @@
test_utils.openControlBox();
var roomspanel = _converse.chatboxviews.get('controlbox').roomspanel;
roomspanel.el.querySelector('.trigger-add-chatrooms-modal').click();
roomspanel.el.querySelector('.show-add-muc-modal').click();
test_utils.closeControlBox(_converse);
const modal = roomspanel.add_room_modal;
test_utils.waitUntil(function () {
@ -3147,7 +3204,7 @@
test_utils.openControlBox();
var roomspanel = _converse.chatboxviews.get('controlbox').roomspanel;
roomspanel.el.querySelector('.trigger-list-chatrooms-modal').click();
roomspanel.el.querySelector('.show-list-muc-modal').click();
test_utils.closeControlBox(_converse);
const modal = roomspanel.list_rooms_modal;
test_utils.waitUntil(function () {

View File

@ -1,6 +1,6 @@
(function (root, factory) {
define(["jquery", "jasmine", "mock", "converse-core", "test-utils"], factory);
} (this, function ($, jasmine, mock, converse, test_utils) {
define(["jquery", "jasmine", "mock", "test-utils"], factory);
} (this, function ($, jasmine, mock, test_utils) {
var _ = converse.env._;
var $pres = converse.env.$pres;
var $msg = converse.env.$msg;

View File

@ -2,10 +2,9 @@
define([
"jquery",
"jasmine",
"converse-core",
"mock",
"test-utils"], factory);
} (this, function ($, jasmine, converse, mock, test_utils) {
} (this, function ($, jasmine, mock, test_utils) {
var b64_sha1 = converse.env.b64_sha1;
var _ = converse.env._;

View File

@ -2,10 +2,9 @@
define([
"jasmine",
"jquery",
"converse-core",
"mock",
"test-utils"], factory);
} (this, function (jasmine, $, converse, mock, test_utils) {
} (this, function (jasmine, $, mock, test_utils) {
"use strict";
var Strophe = converse.env.Strophe;
var $iq = converse.env.$iq;
@ -15,7 +14,11 @@
describe("Whenever converse.js queries a server for its features", function () {
it("stores the features it receives", mock.initConverseWithAsync(function (done, _converse) {
it("stores the features it receives",
mock.initConverseWithPromises(
null, ['discoInitialized'], {},
function (done, _converse) {
var IQ_stanzas = _converse.connection.IQ_stanzas;
var IQ_ids = _converse.connection.IQ_ids;
test_utils.waitUntil(function () {

View File

@ -1,6 +1,6 @@
(function (root, factory) {
define(["jasmine", "mock", "converse-core", "test-utils"], factory);
} (this, function (jasmine, mock, converse, test_utils) {
define(["jasmine", "mock", "test-utils"], factory);
} (this, function (jasmine, mock, test_utils) {
return describe("The _converse Event Emitter", function() {

View File

@ -2,15 +2,14 @@
define([
"jasmine",
"jquery",
"converse-core",
"utils",
"mock",
"test-utils"
], factory);
} (this, function (jasmine, $, converse, utils, mock, test_utils) {
} (this, function (jasmine, $, mock, test_utils) {
"use strict";
var $msg = converse.env.$msg,
_ = converse.env._;
_ = converse.env._,
utils = converse.env.utils;
describe("A headlines box", function () {

View File

@ -1,11 +1,9 @@
(function (root, factory) {
define([
"jasmine",
"jquery",
"converse-core",
"mock",
"test-utils"], factory);
} (this, function (jasmine, $, converse, mock, test_utils) {
} (this, function (jasmine, mock, test_utils) {
"use strict";
var Strophe = converse.env.Strophe;
var $iq = converse.env.$iq;
@ -367,6 +365,7 @@
}).then(function () {
// Check that the image renders
expect(view.el.querySelector('.chat-msg .chat-msg-media').innerHTML.trim()).toEqual(
'<!-- src/templates/image.html -->\n'+
'<a href="http://localhost:8000/logo/conversejs-filled.svg" target="_blank" rel="noopener">'+
'<img class="chat-image img-thumbnail" src="http://localhost:8000/logo/conversejs-filled.svg">'+
'</a>');
@ -474,6 +473,7 @@
}).then(function () {
// Check that the image renders
expect(view.el.querySelector('.chat-msg .chat-msg-media').innerHTML.trim()).toEqual(
'<!-- src/templates/image.html -->\n'+
'<a href="http://localhost:8000/logo/conversejs-filled.svg" target="_blank" rel="noopener">'+
'<img class="chat-image img-thumbnail" src="http://localhost:8000/logo/conversejs-filled.svg"></a>')
XMLHttpRequest.prototype.send = send_backup;

View File

@ -1,6 +1,6 @@
(function (root, factory) {
define(["jasmine", "mock", "converse-core", "test-utils"], factory);
} (this, function (jasmine, mock, converse, test_utils) {
define(["jasmine", "mock", "test-utils"], factory);
} (this, function (jasmine, mock, test_utils) {
describe("The Login Form", function () {

View File

@ -1,6 +1,6 @@
(function (root, factory) {
define(["jasmine", "mock", "converse-core", "test-utils"], factory);
} (this, function (jasmine, mock, converse, test_utils) {
define(["jasmine", "mock", "test-utils"], factory);
} (this, function (jasmine, mock, test_utils) {
"use strict";
var _ = converse.env._;
var Backbone = converse.env.Backbone;

View File

@ -2,12 +2,10 @@
define([
"jquery",
"jasmine",
"utils",
"converse-core",
"mock",
"test-utils"
], factory);
} (this, function ($, jasmine, utils, converse, mock, test_utils) {
} (this, function ($, jasmine, mock, test_utils) {
"use strict";
var _ = converse.env._;
var $iq = converse.env.$iq;
@ -62,7 +60,7 @@
expect(msg_obj.get('message')).toEqual(message);
expect(msg_obj.get('fullname')).toEqual(mock.cur_names[0]);
expect(msg_obj.get('sender')).toEqual('them');
expect(msg_obj.get('delayed')).toEqual(false);
expect(msg_obj.get('is_delayed')).toEqual(false);
// Now check that the message appears inside the chatbox in the DOM
var chat_content = chatboxview.el.querySelector('.chat-content');
expect(chat_content.querySelector('.chat-msg .chat-msg-text').textContent).toEqual(message);
@ -178,7 +176,7 @@
expect(msg_obj.get('message')).toEqual(message);
expect(msg_obj.get('fullname')).toEqual(undefined);
expect(msg_obj.get('sender')).toEqual('them');
expect(msg_obj.get('delayed')).toEqual(false);
expect(msg_obj.get('is_delayed')).toEqual(false);
// Now check that the message appears inside the chatbox in the DOM
var chat_content = chatboxview.el.querySelector('.chat-content');
expect(chat_content.querySelector('.chat-msg .chat-msg-text').textContent).toEqual(message);
@ -579,7 +577,7 @@
expect($el.hasClass('chat-msg-followup')).toBe(false);
expect($el.text()).toEqual('Older message');
$time = $chat_content.find('time:eq(1)');
$time = $chat_content.find('time.separator-text:eq(1)');
expect($time.text()).toEqual("Monday Jan 1st 2018");
$day = $chat_content.find('.date-separator:eq(1)');
@ -593,7 +591,7 @@
expect($el.find('.chat-msg-text').text()).toEqual('another inbetween message');
expect($el.hasClass('chat-msg-followup')).toBe(true);
$time = $chat_content.find('time:nth(2)');
$time = $chat_content.find('time.separator-text:nth(2)');
expect($time.text()).toEqual("Tuesday Jan 2nd 2018");
$day = $chat_content.find('.date-separator:nth(2)');
@ -630,7 +628,7 @@
*/
sinon.spy(_converse, 'log');
sinon.spy(_converse.chatboxes, 'getChatBox');
sinon.spy(utils, 'isHeadlineMessage');
sinon.spy(u, 'isHeadlineMessage');
var msg = $msg({
from: 'localhost',
to: _converse.bare_jid,
@ -642,13 +640,13 @@
"onMessage: Ignoring incoming headline message sent with type 'chat' from JID: localhost",
Strophe.LogLevel.INFO
)).toBeTruthy();
expect(utils.isHeadlineMessage.called).toBeTruthy();
expect(utils.isHeadlineMessage.returned(true)).toBeTruthy();
expect(u.isHeadlineMessage.called).toBeTruthy();
expect(u.isHeadlineMessage.returned(true)).toBeTruthy();
expect(_converse.chatboxes.getChatBox.called).toBeFalsy();
// Remove sinon spies
_converse.log.restore();
_converse.chatboxes.getChatBox.restore();
utils.isHeadlineMessage.restore();
u.isHeadlineMessage.restore();
done();
}));
@ -692,7 +690,7 @@
expect(msg_obj.get('message')).toEqual(msgtext);
expect(msg_obj.get('fullname')).toEqual(mock.cur_names[1]);
expect(msg_obj.get('sender')).toEqual('them');
expect(msg_obj.get('delayed')).toEqual(false);
expect(msg_obj.get('is_delayed')).toEqual(false);
// Now check that the message appears inside the chatbox in the DOM
var chat_content = chatboxview.el.querySelector('.chat-content');
expect(chat_content.querySelector('.chat-msg .chat-msg-text').textContent).toEqual(msgtext);
@ -745,7 +743,7 @@
expect(msg_obj.get('message')).toEqual(msgtext);
expect(msg_obj.get('fullname')).toEqual(_converse.xmppstatus.get('fullname'));
expect(msg_obj.get('sender')).toEqual('me');
expect(msg_obj.get('delayed')).toEqual(false);
expect(msg_obj.get('is_delayed')).toEqual(false);
// Now check that the message appears inside the chatbox in the DOM
var $chat_content = $(chatboxview.el).find('.chat-content');
var msg_txt = $chat_content.find('.chat-msg').find('.chat-msg-text').text();
@ -901,7 +899,7 @@
expect(msg_obj.get('message')).toEqual(message);
expect(msg_obj.get('fullname')).toEqual(contact_name);
expect(msg_obj.get('sender')).toEqual('them');
expect(msg_obj.get('delayed')).toEqual(true);
expect(msg_obj.get('is_delayed')).toEqual(true);
return test_utils.waitUntil(() => chatbox.vcard.get('fullname') === 'Candice van der Knijff')
.then(function () {
@ -930,7 +928,7 @@
expect(_converse.emit).toHaveBeenCalledWith('message', jasmine.any(Object));
// Check that there is a <time> element, with the required
// props.
expect($chat_content[0].querySelectorAll('time').length).toEqual(2); // There are now two time elements
expect($chat_content[0].querySelectorAll('time.separator-text').length).toEqual(2); // There are now two time elements
var message_date = new Date();
$day = $chat_content.find('.date-separator:last');
@ -947,7 +945,7 @@
expect(msg_obj.get('message')).toEqual(message);
expect(msg_obj.get('fullname')).toEqual(contact_name);
expect(msg_obj.get('sender')).toEqual('them');
expect(msg_obj.get('delayed')).toEqual(false);
expect(msg_obj.get('is_delayed')).toEqual(false);
msg_txt = $chat_content.find('.chat-msg').last().find('.chat-msg-text').text();
expect(msg_txt).toEqual(message);
@ -1069,6 +1067,43 @@
done();
}));
it("will render newlines", mock.initConverseWithPromises(null, ['rosterGroupsFetched'], {}, function (done, _converse) {
test_utils.createContacts(_converse, 'current');
const contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
test_utils.openChatBoxFor(_converse, contact_jid);
let stanza = Strophe.xmlHtmlNode(
"<message from='"+contact_jid+"'"+
" type='chat'"+
" to='dummy@localhost/resource'>"+
" <body>Hey\nHave you heard the news?</body>"+
"</message>").firstChild;
_converse.connection._dataRecv(test_utils.createRequest(stanza));
const view = _converse.chatboxviews.get(contact_jid);
const chat_content = view.el.querySelector('.chat-content');
expect(chat_content.querySelector('.chat-msg-text').innerHTML).toBe('Hey<br>Have you heard the news?');
stanza = Strophe.xmlHtmlNode(
"<message from='"+contact_jid+"'"+
" type='chat'"+
" to='dummy@localhost/resource'>"+
" <body>Hey\n\n\nHave you heard the news?</body>"+
"</message>").firstChild;
_converse.connection._dataRecv(test_utils.createRequest(stanza));
expect(chat_content.querySelector('.message:last-child .chat-msg-text').innerHTML).toBe('Hey<br><br>Have you heard the news?');
stanza = Strophe.xmlHtmlNode(
"<message from='"+contact_jid+"'"+
" type='chat'"+
" to='dummy@localhost/resource'>"+
" <body>Hey\nHave you heard\nthe news?</body>"+
"</message>").firstChild;
_converse.connection._dataRecv(test_utils.createRequest(stanza));
expect(chat_content.querySelector('.message:last-child .chat-msg-text').innerHTML).toBe('Hey<br>Have you heard<br>the news?');
done();
}));
it("will render images from their URLs",
mock.initConverseWithPromises(
null, ['rosterGroupsFetched'], {},
@ -1083,12 +1118,12 @@
spyOn(view.model, 'sendMessage').and.callThrough();
test_utils.sendMessage(view, message);
test_utils.waitUntil(function () {
return view.el.querySelectorAll('.chat-content .chat-image').length;
}, 1000).then(function () {
test_utils.waitUntil(() => view.el.querySelectorAll('.chat-content .chat-image').length)
.then(() => {
expect(view.model.sendMessage).toHaveBeenCalled();
var msg = $(view.el).find('.chat-content .chat-msg').last().find('.chat-msg-text');
expect(msg.html().trim()).toEqual(
'<!-- src/templates/image.html -->\n'+
'<a href="'+base_url+'/logo/conversejs-filled.svg" target="_blank" rel="noopener"><img class="chat-image img-thumbnail"'+
' src="' + message + '"></a>');
message += "?param1=val1&param2=val2";
@ -1096,10 +1131,11 @@
return test_utils.waitUntil(function () {
return view.el.querySelectorAll('.chat-content .chat-image').length === 2;
}, 1000);
}).then(function () {
}).then(() => {
expect(view.model.sendMessage).toHaveBeenCalled();
var msg = $(view.el).find('.chat-content').find('.chat-msg').last().find('.chat-msg-text');
expect(msg.html().trim()).toEqual(
'<!-- src/templates/image.html -->\n'+
'<a href="'+base_url+'/logo/conversejs-filled.svg?param1=val1&amp;param2=val2" target="_blank" rel="noopener"><img'+
' class="chat-image img-thumbnail" src="'+message.replace(/&/g, '&amp;')+'"></a>')
@ -1342,6 +1378,7 @@
expect(msg.outerHTML).toEqual('<span class="chat-msg-text">Have you heard this funny audio?</span>');
var media = view.el.querySelector('.chat-msg .chat-msg-media');
expect(media.innerHTML.replace(/(\r\n|\n|\r)/gm, "")).toEqual(
'<!-- src/templates/audio.html -->'+
'<audio controls=""><source src="http://localhost/audio.mp3" type="audio/mpeg"></audio>'+
'<a target="_blank" rel="noopener" href="http://localhost/audio.mp3">Download audio file</a>');
@ -1359,6 +1396,7 @@
expect(msg.innerHTML).toEqual('');
media = view.el.querySelector('.chat-msg:last-child .chat-msg-media');
expect(media.innerHTML.replace(/(\r\n|\n|\r)/gm, "")).toEqual(
'<!-- src/templates/audio.html -->'+
'<audio controls=""><source src="http://localhost/audio.mp3" type="audio/mpeg"></audio>'+
'<a target="_blank" rel="noopener" href="http://localhost/audio.mp3">Download audio file</a>');
done();
@ -1392,6 +1430,7 @@
expect(msg.outerHTML).toEqual('<span class="chat-msg-text">Have you seen this funny video?</span>');
var media = view.el.querySelector('.chat-msg .chat-msg-media');
expect(media.innerHTML.replace(/(\r\n|\n|\r)/gm, "")).toEqual(
'<!-- src/templates/video.html -->'+
'<video controls=""><source src="http://localhost/video.mp4" type="video/mp4"></video>'+
'<a target="_blank" rel="noopener" href="http://localhost/video.mp4">Download video file</a>');
@ -1409,6 +1448,7 @@
expect(msg.innerHTML).toEqual('');
media = view.el.querySelector('.chat-msg:last-child .chat-msg-media');
expect(media.innerHTML.replace(/(\r\n|\n|\r)/gm, "")).toEqual(
'<!-- src/templates/video.html -->'+
'<video controls=""><source src="http://localhost/video.mp4" type="video/mp4"></video>'+
'<a target="_blank" rel="noopener" href="http://localhost/video.mp4">Download video file</a>');
done();
@ -1442,6 +1482,7 @@
expect(msg.outerHTML).toEqual('<span class="chat-msg-text">Have you downloaded this funny file?</span>');
var media = view.el.querySelector('.chat-msg .chat-msg-media');
expect(media.innerHTML.replace(/(\r\n|\n|\r)/gm, "")).toEqual(
'<!-- src/templates/file.html -->'+
'<a target="_blank" rel="noopener" href="http://localhost/funny.pdf">Download: "funny.pdf</a>');
done();
});
@ -1476,6 +1517,7 @@
expect(msg.outerHTML).toEqual('<span class="chat-msg-text">Have you seen this funny image?</span>');
var media = view.el.querySelector('.chat-msg .chat-msg-media');
expect(media.innerHTML.replace(/(\r\n|\n|\r)/gm, "")).toEqual(
'<!-- src/templates/image.html -->'+
'<a href="http://localhost:8000/logo/conversejs-filled.svg" target="_blank" rel="noopener">'+
'<img class="chat-image img-thumbnail" src="http://localhost:8000/logo/conversejs-filled.svg">'+
'</a>');

View File

@ -1,6 +1,6 @@
(function (root, factory) {
define(["jquery", "jasmine", "mock", "converse-core", "test-utils"], factory);
} (this, function ($, jasmine, mock, converse, test_utils) {
define(["jquery", "jasmine", "mock", "test-utils"], factory);
} (this, function ($, jasmine, mock, test_utils) {
var _ = converse.env._;
var $msg = converse.env.$msg;

View File

@ -1,6 +1,6 @@
(function (root, factory) {
define(["jquery", "jasmine", "mock", "converse-core", "test-utils", "utils"], factory);
} (this, function ($, jasmine, mock, converse, test_utils, utils) {
define(["jquery", "jasmine", "mock", "test-utils"], factory);
} (this, function ($, jasmine, mock, test_utils) {
"use strict";
var _ = converse.env._;
var $msg = converse.env.$msg;

View File

@ -1,6 +1,6 @@
(function (root, factory) {
define(["jasmine", "mock", "converse-core", "test-utils"], factory);
} (this, function (jasmine, mock, converse, test_utils) {
define(["jasmine", "mock", "test-utils"], factory);
} (this, function (jasmine, mock, test_utils) {
var Strophe = converse.env.Strophe;
var b64_sha1 = converse.env.b64_sha1;
var $iq = converse.env.$iq;

View File

@ -1,72 +1,108 @@
(function (root, factory) {
define(["jquery", "jasmine", "mock", "converse-core", "test-utils"], factory);
} (this, function ($, jasmine, mock, converse, test_utils) {
define(["jquery", "jasmine", "mock", "test-utils"], factory);
} (this, function ($, jasmine, mock, test_utils) {
var Strophe = converse.env.Strophe;
var b64_sha1 = converse.env.b64_sha1;
var $pres = converse.env.$pres;
var _ = converse.env._;
describe("A chatbox with an active OTR session", function() {
it("will not show the spoiler toolbar button",
describe("A chatbox", function() {
it("contains a button for starting an encrypted chat session",
mock.initConverseWithPromises(
null, ['rosterGroupsFetched'], {},
function (done, _converse) {
var timeout = true, $toolbar, view;
test_utils.createContacts(_converse, 'current');
var contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
test_utils.openControlBox();
// XXX: We need to send a presence from the contact, so that we
// have a resource, that resource is then queried to see
// whether Strophe.NS.SPOILER is supported, in which case
// the spoiler button will appear.
var presence = $pres({
'from': contact_jid+'/phone',
'to': 'dummy@localhost'
});
_converse.connection._dataRecv(test_utils.createRequest(presence));
test_utils.openChatBoxFor(_converse, contact_jid);
test_utils.waitUntil(function () {
return $(_converse.rosterview.el).find('.roster-group').length;
}, 300).then(function () {
// TODO: More tests can be added here...
var contact_jid = mock.cur_names[2].replace(/ /g,'.').toLowerCase() + '@localhost';
test_utils.openChatBoxFor(_converse, contact_jid);
view = _converse.chatboxviews.get(contact_jid);
$toolbar = $(view.el).find('ul.chat-toolbar');
expect($toolbar.find('.toggle-otr').length).toBe(1);
// Register spies
spyOn(view, 'toggleOTRMenu').and.callThrough();
view.delegateEvents(); // We need to rebind all events otherwise our spy won't be called
test_utils.waitUntilDiscoConfirmed(_converse, contact_jid+'/phone', [], [Strophe.NS.SPOILER]).then(function () {
var spoiler_toggle;
var view = _converse.chatboxviews.get(contact_jid);
spyOn(view, 'addSpoilerButton').and.callThrough();
view.model.set('otr_status', 1);
test_utils.waitUntil(function () {
return _.isNull(view.el.querySelector('.toggle-compose-spoiler'));
}).then(function () {
spoiler_toggle = view.el.querySelector('.toggle-compose-spoiler');
expect(spoiler_toggle).toBe(null);
view.model.set('otr_status', 3);
return test_utils.waitUntil(function () {
return !_.isNull(view.el.querySelector('.toggle-compose-spoiler'));
});
}).then(function () {
spoiler_toggle = view.el.querySelector('.toggle-compose-spoiler');
expect(spoiler_toggle).not.toBe(null);
view.model.set('otr_status', 2);
return test_utils.waitUntil(function () {
return _.isNull(view.el.querySelector('.toggle-compose-spoiler'));
});
}).then(function () {
spoiler_toggle = view.el.querySelector('.toggle-compose-spoiler');
expect(spoiler_toggle).toBe(null);
view.model.set('otr_status', 4);
return test_utils.waitUntil(function () {
return !_.isNull(view.el.querySelector('.toggle-compose-spoiler'));
});
}).then(function () {
spoiler_toggle = view.el.querySelector('.toggle-compose-spoiler');
expect(spoiler_toggle).not.toBe(null);
done();
});
timeout = false;
$toolbar[0].querySelector('.toggle-otr').click();
return test_utils.waitUntil(function () {
return view.el.querySelector('.otr-menu').offsetHeight;
}, 300)
}).then(function () {
expect(view.toggleOTRMenu).toHaveBeenCalled();
done();
});
}));
describe("with an active OTR session", function() {
it("will not show the spoiler toolbar button",
mock.initConverseWithPromises(
null, ['rosterGroupsFetched'], {},
function (done, _converse) {
test_utils.createContacts(_converse, 'current');
var 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
// whether Strophe.NS.SPOILER is supported, in which case
// the spoiler button will appear.
var presence = $pres({
'from': contact_jid+'/phone',
'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 () {
var spoiler_toggle;
var view = _converse.chatboxviews.get(contact_jid);
spyOn(view, 'addSpoilerButton').and.callThrough();
view.model.set('otr_status', 1);
test_utils.waitUntil(function () {
return _.isNull(view.el.querySelector('.toggle-compose-spoiler'));
}).then(function () {
spoiler_toggle = view.el.querySelector('.toggle-compose-spoiler');
expect(spoiler_toggle).toBe(null);
view.model.set('otr_status', 3);
return test_utils.waitUntil(function () {
return !_.isNull(view.el.querySelector('.toggle-compose-spoiler'));
});
}).then(function () {
spoiler_toggle = view.el.querySelector('.toggle-compose-spoiler');
expect(spoiler_toggle).not.toBe(null);
view.model.set('otr_status', 2);
return test_utils.waitUntil(function () {
return _.isNull(view.el.querySelector('.toggle-compose-spoiler'));
});
}).then(function () {
spoiler_toggle = view.el.querySelector('.toggle-compose-spoiler');
expect(spoiler_toggle).toBe(null);
view.model.set('otr_status', 4);
return test_utils.waitUntil(function () {
return !_.isNull(view.el.querySelector('.toggle-compose-spoiler'));
});
}).then(function () {
spoiler_toggle = view.el.querySelector('.toggle-compose-spoiler');
expect(spoiler_toggle).not.toBe(null);
done();
});
});
}));
});
});
describe("The OTR module", function() {

View File

@ -1,5 +1,5 @@
(function (root, factory) {
define(["jasmine", "mock", "converse-core", "test-utils", "converse-ping"], factory);
define(["jasmine", "mock", "test-utils"], factory);
} (this, function (jasmine, mock, test_utils) {
"use strict";

View File

@ -4,15 +4,15 @@
define([
"jasmine",
"jquery",
"converse-core",
"mock",
"test-utils",
"lodash"], factory);
} (this, function (jasmine, $, converse, mock, test_utils, _) {
], factory);
} (this, function (jasmine, $, mock, test_utils) {
"use strict";
var Strophe = converse.env.Strophe;
var $iq = converse.env.$iq;
var $pres = converse.env.$pres;
var _ = converse.env._;
var u = converse.env.utils;
// See: https://xmpp.org/rfcs/rfc3921.html

View File

@ -1,8 +1,9 @@
(function (root, factory) {
define(["jasmine", "mock", "converse-core", "test-utils", "utils"], factory);
} (this, function (jasmine, mock, converse, test_utils, u) {
define(["jasmine", "mock", "test-utils"], factory);
} (this, function (jasmine, mock, test_utils) {
var _ = converse.env._;
var $iq = converse.env.$iq;
var u = converse.env.utils;
describe("Profiling", function() {
xit("adds hundreds of contacts to the roster",

View File

@ -2,10 +2,9 @@
define([
"jasmine",
"jquery",
"converse-core",
"mock",
"test-utils"], factory);
} (this, function (jasmine, $, converse, mock, test_utils) {
} (this, function (jasmine, $, mock, test_utils) {
"use strict";
var Strophe = converse.env.Strophe;
var $iq = converse.env.$iq;
@ -210,7 +209,7 @@
* </iq>
*/
spyOn(_converse.roster, "updateContact").and.callThrough();
stanza = $iq({'type': 'set', 'from': 'dummy@localhost'})
stanza = $iq({'type': 'set', 'from': _converse.connection.jid})
.c('query', {'xmlns': 'jabber:iq:roster'})
.c('item', {
'jid': 'contact@example.org',

164
spec/push.js Normal file
View File

@ -0,0 +1,164 @@
(function (root, factory) {
define(["jasmine", "mock", "test-utils"], factory);
} (this, function (jasmine, mock, test_utils) {
"use strict";
var $iq = converse.env.$iq;
var Strophe = converse.env.Strophe;
var _ = converse.env._;
describe("XEP-0357 Push Notifications", function () {
it("can be enabled",
mock.initConverseWithPromises(null,
['rosterGroupsFetched'], {
'push_app_servers': [{
'jid': 'push-5@client.example',
'node': 'yxs32uqsflafdk3iuqo'
}]
}, function (done, _converse) {
const IQ_stanzas = _converse.connection.IQ_stanzas;
let stanza;
expect(_converse.session.get('push_enabled')).toBeFalsy();
test_utils.waitUntilDiscoConfirmed(
_converse, _converse.push_app_servers[0].jid,
[{'category': 'pubsub', 'type':'push'}],
['urn:xmpp:push:0'], [], 'info')
.then(() => test_utils.waitUntilDiscoConfirmed(
_converse,
_converse.bare_jid,
[{'category': 'account', 'type':'registered'}],
['urn:xmpp:push:0'], [], 'info'))
.then(() => {
return test_utils.waitUntil(() => {
const node = _.filter(IQ_stanzas, function (iq) {
return iq.nodeTree.querySelector('iq[type="set"] enable[xmlns="urn:xmpp:push:0"]');
}).pop();
if (node) {
stanza = node.nodeTree;
return true;
}
})
}).then(() => {
expect(stanza.outerHTML).toEqual(
`<iq type="set" xmlns="jabber:client" id="${stanza.getAttribute('id')}">`+
'<enable xmlns="urn:xmpp:push:0" jid="push-5@client.example" node="yxs32uqsflafdk3iuqo"/>'+
'</iq>'
)
_converse.connection._dataRecv(test_utils.createRequest($iq({
'to': _converse.connection.jid,
'type': 'result',
'id': stanza.getAttribute('id')
})));
return test_utils.waitUntil(() => _converse.session.get('push_enabled'))
}).then(() => {
done();
}).catch(_.partial(_converse.log, _, Strophe.LogLevel.FATAL));
}));
it("can be disabled",
mock.initConverseWithPromises(null,
['rosterGroupsFetched'], {
'push_app_servers': [{
'jid': 'push-5@client.example',
'node': 'yxs32uqsflafdk3iuqo',
'disable': true
}]
}, function (done, _converse) {
const IQ_stanzas = _converse.connection.IQ_stanzas;
let stanza;
expect(_converse.session.get('push_enabled')).toBeFalsy();
test_utils.waitUntilDiscoConfirmed(
_converse,
_converse.bare_jid,
[{'category': 'account', 'type':'registered'}],
['urn:xmpp:push:0'], [], 'info')
.then(() => {
return test_utils.waitUntil(() => {
const node = _.filter(IQ_stanzas, function (iq) {
return iq.nodeTree.querySelector('iq[type="set"] disable[xmlns="urn:xmpp:push:0"]');
}).pop();
if (node) {
stanza = node.nodeTree;
return true;
}
})
}).then(() => {
expect(stanza.outerHTML).toEqual(
`<iq type="set" xmlns="jabber:client" id="${stanza.getAttribute('id')}">`+
'<disable xmlns="urn:xmpp:push:0" jid="push-5@client.example" node="yxs32uqsflafdk3iuqo"/>'+
'</iq>'
)
_converse.connection._dataRecv(test_utils.createRequest($iq({
'to': _converse.connection.jid,
'type': 'result',
'id': stanza.getAttribute('id')
})));
return test_utils.waitUntil(() => _converse.session.get('push_enabled'))
}).then(() => {
done();
}).catch(_.partial(_converse.log, _, Strophe.LogLevel.FATAL));
}));
it("can require a secret token to be included",
mock.initConverseWithPromises(null,
['rosterGroupsFetched'], {
'push_app_servers': [{
'jid': 'push-5@client.example',
'node': 'yxs32uqsflafdk3iuqo',
'secret': 'eruio234vzxc2kla-91'
}]
}, function (done, _converse) {
const IQ_stanzas = _converse.connection.IQ_stanzas;
let stanza;
expect(_converse.session.get('push_enabled')).toBeFalsy();
test_utils.waitUntilDiscoConfirmed(
_converse, _converse.push_app_servers[0].jid,
[{'category': 'pubsub', 'type':'push'}],
['urn:xmpp:push:0'], [], 'info')
.then(() => test_utils.waitUntilDiscoConfirmed(
_converse,
_converse.bare_jid,
[{'category': 'account', 'type':'registered'}],
['urn:xmpp:push:0'], [], 'info'))
.then(() => {
return test_utils.waitUntil(() => {
const node = _.filter(IQ_stanzas, function (iq) {
return iq.nodeTree.querySelector('iq[type="set"] enable[xmlns="urn:xmpp:push:0"]');
}).pop();
if (node) {
stanza = node.nodeTree;
return true;
}
})
}).then(() => {
expect(stanza.outerHTML).toEqual(
`<iq type="set" xmlns="jabber:client" id="${stanza.getAttribute('id')}">`+
'<enable xmlns="urn:xmpp:push:0" jid="push-5@client.example" node="yxs32uqsflafdk3iuqo">'+
'<x xmlns="jabber:x:data" type="submit">'+
'<field var="FORM_TYPE"><value>http://jabber.org/protocol/pubsub#publish-options</value></field>'+
'<field var="secret"><value>eruio234vzxc2kla-91</value></field>'+
'</x>'+
'</enable>'+
'</iq>'
)
_converse.connection._dataRecv(test_utils.createRequest($iq({
'to': _converse.connection.jid,
'type': 'result',
'id': stanza.getAttribute('id')
})));
return test_utils.waitUntil(() => _converse.session.get('push_enabled'))
}).then(() => {
done();
}).catch(_.partial(_converse.log, _, Strophe.LogLevel.FATAL));
}));
});
}));

View File

@ -1,6 +1,6 @@
(function (root, factory) {
define(["jquery", "jasmine", "mock", "converse-core", "test-utils"], factory);
} (this, function ($, jasmine, mock, converse, test_utils) {
define(["jquery", "jasmine", "mock", "test-utils"], factory);
} (this, function ($, jasmine, mock, test_utils) {
var Strophe = converse.env.Strophe;
var $iq = converse.env.$iq;
var _ = converse.env._;
@ -264,10 +264,24 @@
registerview.el.querySelector('input[type=submit]').click();
expect(_converse.connection.send).toHaveBeenCalled();
var $stanza = $(_converse.connection.send.calls.argsFor(0)[0].tree());
expect($stanza.children('query').children().length).toBe(1);
expect($stanza.children('query').children().children().length).toBe(3);
expect($stanza.children('query').children().children()[0].tagName).toBe('field');
stanza = _converse.connection.send.calls.argsFor(0)[0].tree();
expect(stanza.outerHTML.trim().replace(/(\n|\s{2,})/g, '')).toEqual(
'<iq type="set" id="'+stanza.getAttribute('id')+'" xmlns="jabber:client">'+
'<query xmlns="jabber:iq:register">'+
'<x xmlns="jabber:x:data" type="submit">'+
'<field xmlns="http://www.w3.org/1999/xhtml" var="username">'+
'<value>testusername</value>'+
'</field>'+
'<field xmlns="http://www.w3.org/1999/xhtml" var="password">'+
'<value>testpassword</value>'+
'</field>'+
'<field xmlns="http://www.w3.org/1999/xhtml" var="email">'+
'<value>test@email.local</value>'+
'</field>'+
'</x>'+
'</query>'+
'</iq>'
);
done();
});
}));

View File

@ -1,13 +1,17 @@
(function (root, factory) {
define(["jasmine", "mock", "converse-core", "converse-roomslist", "test-utils"], factory);
} (this, function (jasmine, mock, converse, roomslist, test_utils) {
define(["jasmine", "mock", "test-utils"], factory);
} (this, function (jasmine, mock, test_utils) {
var _ = converse.env._;
var $msg = converse.env.$msg;
var $iq = converse.env.$iq;
var $pres = converse.env.$pres;
var Promise = converse.env.Promise;
var Strophe = converse.env.Strophe;
var u = converse.env.utils;
describe("The converse-roomslist plugin", function () {
describe("A list of open rooms", function () {
it("is shown under a list of open rooms in the \"Rooms\" panel", mock.initConverseWithPromises(
it("is shown in the \"Rooms\" panel", mock.initConverseWithPromises(
null, ['rosterGroupsFetched'],
{ allow_bookmarks: false // Makes testing easier, otherwise we
// have to mock stanza traffic.
@ -50,7 +54,104 @@
));
});
describe("An room shown in the rooms list", function () {
describe("A room shown in the rooms list", function () {
it("has an info icon which opens a details modal when clicked", mock.initConverseWithPromises(
null, ['rosterGroupsFetched'],
{ whitelisted_plugins: ['converse-roomslist'],
allow_bookmarks: false // Makes testing easier, otherwise we
// have to mock stanza traffic.
}, function (done, _converse) {
test_utils.openControlBox();
_converse.api.rooms.open('coven@chat.shakespeare.lit', {'nick': 'some1'});
const view = _converse.chatboxviews.get('coven@chat.shakespeare.lit');
const last_stanza = _.last(_converse.connection.IQ_stanzas).nodeTree;
const IQ_id = last_stanza.getAttribute('id');
const features_stanza = $iq({
'from': 'coven@chat.shakespeare.lit',
'id': IQ_id,
'to': 'dummy@localhost/desktop',
'type': 'result'
})
.c('query', { 'xmlns': 'http://jabber.org/protocol/disco#info'})
.c('identity', {
'category': 'conference',
'name': 'A Dark Cave',
'type': 'text'
}).up()
.c('feature', {'var': 'http://jabber.org/protocol/muc'}).up()
.c('feature', {'var': 'muc_passwordprotected'}).up()
.c('feature', {'var': 'muc_hidden'}).up()
.c('feature', {'var': 'muc_temporary'}).up()
.c('feature', {'var': 'muc_open'}).up()
.c('feature', {'var': 'muc_unmoderated'}).up()
.c('feature', {'var': 'muc_nonanonymous'}).up()
.c('feature', {'var': 'urn:xmpp:mam:0'}).up()
.c('x', { 'xmlns':'jabber:x:data', 'type':'result'})
.c('field', {'var':'FORM_TYPE', 'type':'hidden'})
.c('value').t('http://jabber.org/protocol/muc#roominfo').up().up()
.c('field', {'type':'text-single', 'var':'muc#roominfo_description', 'label':'Description'})
.c('value').t('This is the description').up().up()
.c('field', {'type':'text-single', 'var':'muc#roominfo_occupants', 'label':'Number of occupants'})
.c('value').t(0);
_converse.connection._dataRecv(test_utils.createRequest(features_stanza));
test_utils.waitUntil(() => view.model.get('connection_status') === converse.ROOMSTATUS.CONNECTING)
.then(function () {
var presence = $pres({
to: _converse.connection.jid,
from: 'coven@chat.shakespeare.lit/some1',
id: 'DC352437-C019-40EC-B590-AF29E879AF97'
}).c('x').attrs({xmlns:'http://jabber.org/protocol/muc#user'})
.c('item').attrs({
affiliation: 'member',
jid: _converse.bare_jid,
role: 'participant'
}).up()
.c('status').attrs({code:'110'});
_converse.connection._dataRecv(test_utils.createRequest(presence));
const room_els = _converse.rooms_list_view.el.querySelectorAll(".open-room");
expect(room_els.length).toBe(1);
var info_el = _converse.rooms_list_view.el.querySelector(".room-info");
info_el.click();
const modal = view.model.room_details_modal;
return test_utils.waitUntil(() => u.isVisible(modal.el), 2000);
}).then(() => {
const modal = view.model.room_details_modal;
let els = modal.el.querySelectorAll('p.room-info');
expect(els[0].textContent).toBe("Room address (JID): coven@chat.shakespeare.lit")
expect(els[1].textContent).toBe("Name: A Dark Cave")
expect(els[2].textContent).toBe("Description: This is the description")
expect(els[3].textContent).toBe("Online users: 1")
const features_list = modal.el.querySelector('.features-list');
expect(features_list.textContent.replace(/(\n|\s{2,})/g, '')).toBe(
'Password protected - This room requires a password before entry'+
'Hidden - This room is not publicly searchable'+
'Open - Anyone can join this room'+
'Temporary - This room will disappear once the last person leaves'+
'Not anonymous - All other room occupants can see your XMPP username'+
'Not moderated - This room is not being moderated'
);
const presence = $pres({
to: 'dummy@localhost/_converse.js-29092160',
from: 'coven@chat.shakespeare.lit/newguy'
})
.c('x', {xmlns: Strophe.NS.MUC_USER})
.c('item', {
'affiliation': 'none',
'jid': 'newguy@localhost/_converse.js-290929789',
'role': 'participant'
});
_converse.connection._dataRecv(test_utils.createRequest(presence));
els = modal.el.querySelectorAll('p.room-info');
expect(els[3].textContent).toBe("Online users: 2")
done();
});
}));
it("can be closed", mock.initConverseWithPromises(
null, ['rosterGroupsFetched'],

View File

@ -1,6 +1,6 @@
(function (root, factory) {
define(["jquery", "jasmine", "mock", "converse-core", "test-utils"], factory);
} (this, function ($, jasmine, mock, converse, test_utils) {
define(["jquery", "jasmine", "mock", "test-utils"], factory);
} (this, function ($, jasmine, mock, test_utils) {
var _ = converse.env._;
var Strophe = converse.env.Strophe;
var $pres = converse.env.$pres;
@ -35,6 +35,68 @@
describe("The Contacts Roster", function () {
it("supports roster versioning",
mock.initConverseWithPromises(
null, ['rosterGroupsFetched'], {},
function (done, _converse) {
var IQ_stanzas = _converse.connection.IQ_stanzas;
var stanza;
test_utils.waitUntil(() => {
const node = _.filter(IQ_stanzas, function (iq) {
return iq.nodeTree.querySelector('iq query[xmlns="jabber:iq:roster"]');
}).pop();
if (node) {
stanza = node.nodeTree;
return true;
}
}).then(() => {
expect(_converse.roster.data.get('version')).toBeUndefined();
expect(stanza.outerHTML).toBe(
`<iq type="get" id="${stanza.getAttribute('id')}" xmlns="jabber:client">`+
`<query xmlns="jabber:iq:roster"/>`+
`</iq>`);
let result = $iq({
'to': _converse.connection.jid,
'type': 'result',
'id': stanza.getAttribute('id')
}).c('query', {
'xmlns': 'jabber:iq:roster',
'ver': 'ver7'
}).c('item', {'jid': 'nurse@example.com'}).up()
.c('item', {'jid': 'romeo@example.com'})
_converse.connection._dataRecv(test_utils.createRequest(result));
expect(_converse.roster.data.get('version')).toBe('ver7');
expect(_converse.roster.models.length).toBe(2);
_converse.roster.fetchFromServer();
stanza = _converse.connection.IQ_stanzas.pop().nodeTree;
expect(stanza.outerHTML).toBe(
`<iq type="get" id="${stanza.getAttribute('id')}" xmlns="jabber:client">`+
`<query xmlns="jabber:iq:roster" ver="ver7"/>`+
`</iq>`);
result = $iq({
'to': _converse.connection.jid,
'type': 'result',
'id': stanza.getAttribute('id')
});
_converse.connection._dataRecv(test_utils.createRequest(result));
const roster_push = $iq({
'to': _converse.connection.jid,
'type': 'set',
}).c('query', {'xmlns': 'jabber:iq:roster', 'ver': 'ver34'})
.c('item', {'jid': 'romeo@example.com', 'subscription': 'remove'});
_converse.connection._dataRecv(test_utils.createRequest(roster_push));
expect(_converse.roster.data.get('version')).toBe('ver34');
expect(_converse.roster.models.length).toBe(1);
expect(_converse.roster.at(0).get('jid')).toBe('nurse@example.com');
done();
});
}));
describe("The live filter", function () {
it("will only appear when roster contacts flow over the visible area",

View File

@ -1,13 +1,6 @@
(function (root, factory) {
define([
"jasmine",
"utils",
"mock",
"converse-core",
"test-utils"
], factory);
} (this, function (jasmine, utils, mock, converse, test_utils) {
define(["jasmine", "mock", "test-utils"], factory);
} (this, function (jasmine, mock, test_utils) {
var _ = converse.env._;
var Strophe = converse.env.Strophe;
var $msg = converse.env.$msg;

View File

@ -1,12 +1,10 @@
(function (root, factory) {
define([
"jasmine",
"utils",
"converse-core",
"mock",
"test-utils"
], factory);
} (this, function (jasmine, utils, converse, mock, test_utils) {
} (this, function (jasmine, mock, test_utils) {
"use strict";
var _ = converse.env._;
var $iq = converse.env.$iq;

View File

@ -1,6 +1,6 @@
(function (root, factory) {
define(["jasmine", "converse-core"], factory);
} (this, function (jasmine, converse) {
define(["jasmine"], factory);
} (this, function (jasmine) {
var utils = converse.env.utils,
_ = converse.env._;

View File

@ -1,6 +1,6 @@
(function (root, factory) {
define(["jquery", "jasmine", "mock", "converse-core", "test-utils"], factory);
} (this, function ($, jasmine, mock, converse, test_utils) {
define(["jquery", "jasmine", "mock", "test-utils"], factory);
} (this, function ($, jasmine, mock, test_utils) {
return describe("The XMPPStatus model", function() {

View File

@ -1,9 +0,0 @@
({
baseUrl: "../",
name: "almond",
mainConfigFile: 'config.js',
wrap: {
startFile: "start.frag",
endFile: "end.frag"
}
});

View File

@ -1,54 +0,0 @@
({
baseUrl: "../",
name: "almond",
out: "../dist/converse-no-dependencies.min.js",
include: ["converse"],
exclude: [
"awesomplete",
"backbone.browserStorage",
"backbone.overview",
"moment",
"strophe",
"strophe.rsm",
"strophe.ping",
"otr",
"lodash",
"lodash.converter",
"lodash.noconflict",
"es6-promise"
],
paths: {
"backbone.vdomview": "builds/backbone.vdomview",
"converse-bookmarks": "builds/converse-bookmarks",
"converse-chatboxes": "builds/converse-chatboxes",
"converse-chatview": "builds/converse-chatview",
"converse-controlbox": "builds/converse-controlbox",
"converse-core": "builds/converse-core",
"converse-disco": "builds/converse-disco",
"converse-dragresize": "builds/converse-dragresize",
"converse-embedded": "builds/converse-embedded",
"converse-fullscreen": "builds/converse-fullscreen",
"converse-headline": "builds/converse-headline",
"converse-mam": "builds/converse-mam",
"converse-minimize": "builds/converse-minimize",
"converse-muc": "builds/converse-muc",
"converse-notification": "builds/converse-notification",
"converse-otr": "builds/converse-otr",
"converse-ping": "builds/converse-ping",
"converse-profile": "builds/converse-profile",
"converse-register": "builds/converse-register",
"converse-roomslist": "builds/converse-roomslist",
"converse-rosterview": "builds/converse-rosterview",
"converse-singleton": "builds/converse-singleton",
"converse-vcard": "builds/converse-vcard",
"form-utils": "builds/utils/form",
"i18n": "builds/i18n",
"utils": "builds/utils/core",
"muc-utils": "builds/utils/muc"
},
wrap: {
startFile: "start.frag",
endFile: "end-no-dependencies.frag"
},
mainConfigFile: "config.js"
});

View File

@ -1,43 +0,0 @@
({
baseUrl: "../",
name: "almond",
mainConfigFile: 'config.js',
paths: {
"backbone.vdomview": "builds/backbone.vdomview",
"converse-bookmarks": "builds/converse-bookmarks",
"converse-caps": "builds/converse-caps",
"converse-chatboxes": "builds/converse-chatboxes",
"converse-chatview": "builds/converse-chatview",
"converse-controlbox": "builds/converse-controlbox",
"converse-core": "builds/converse-core",
"converse-disco": "builds/converse-disco",
"converse-dragresize": "builds/converse-dragresize",
"converse-embedded": "builds/converse-embedded",
"converse-fullscreen": "builds/converse-fullscreen",
"converse-headline": "builds/converse-headline",
"converse-mam": "builds/converse-mam",
"converse-message-view": "builds/converse-message-view",
"converse-minimize": "builds/converse-minimize",
"converse-modal": "builds/converse-modal",
"converse-muc": "builds/converse-muc",
"converse-muc-views": "builds/converse-muc-views",
"converse-notification": "builds/converse-notification",
"converse-otr": "builds/converse-otr",
"converse-ping": "builds/converse-ping",
"converse-profile": "builds/converse-profile",
"converse-register": "builds/converse-register",
"converse-roomslist": "builds/converse-roomslist",
"converse-roster": "builds/converse-roster",
"converse-rosterview": "builds/converse-rosterview",
"converse-singleton": "builds/converse-singleton",
"converse-vcard": "builds/converse-vcard",
"form-utils": "builds/utils/form",
"i18n": "builds/i18n",
"muc-utils": "builds/utils/muc",
"utils": "builds/utils/core"
},
wrap: {
startFile: "start.frag",
endFile: "end.frag"
}
});

View File

@ -1,168 +0,0 @@
var config;
if (typeof(require) === 'undefined') {
/* XXX: Hack to work around r.js's stupid parsing.
* We want to save the configuration in a variable so that we can reuse it in
* tests/main.js.
*/
// eslint-disable-next-line
require = { // jshint ignore:line
config: function (c) {
config = c;
}
};
}
require.config({
baseUrl: '.',
paths: {
"IPv6": "node_modules/urijs/src/IPv6",
"SecondLevelDomains": "node_modules/urijs/src/SecondLevelDomains",
"almond": "node_modules/almond/almond",
"awesomplete": "node_modules/awesomplete-avoid-xss/awesomplete",
"babel": "node_modules/requirejs-babel/babel-5.8.34.min",
"backbone": "node_modules/backbone/backbone",
"backbone.browserStorage": "node_modules/backbone.browserStorage/backbone.browserStorage",
"backbone.nativeview": "node_modules/backbone.nativeview/backbone.nativeview",
"backbone.noconflict": "src/backbone.noconflict",
"backbone.orderedlistview": "node_modules/backbone.overview/dist/backbone.orderedlistview",
"backbone.overview": "node_modules/backbone.overview/dist/backbone.overview",
"backbone.vdomview": "node_modules/backbone.vdomview/dist/backbone.vdomview",
"bootstrap": "node_modules/bootstrap.native/dist/bootstrap-native-v4",
"emojione": "node_modules/emojione/lib/js/emojione",
"es6-promise": "node_modules/es6-promise/dist/es6-promise.auto",
"eventemitter": "node_modules/otr/build/dep/eventemitter",
"filesize": "node_modules/filesize/lib/filesize",
"form-utils": "src/utils/form",
"i18n": "src/i18n",
"jed": "node_modules/jed/jed",
"jquery": "src/jquery-stub",
"lodash": "node_modules/lodash/lodash",
"lodash.converter": "3rdparty/lodash.fp",
"lodash.fp": "src/lodash.fp",
"lodash.noconflict": "src/lodash.noconflict",
"message-utils": "src/utils/message",
"muc-utils": "src/utils/muc",
"pluggable": "node_modules/pluggable.js/dist/pluggable",
"polyfill": "src/polyfill",
"punycode": "node_modules/urijs/src/punycode",
"sizzle": "node_modules/sizzle/dist/sizzle",
"snabbdom": "node_modules/snabbdom/dist/snabbdom",
"snabbdom-attributes": "node_modules/snabbdom/dist/snabbdom-attributes",
"snabbdom-class": "node_modules/snabbdom/dist/snabbdom-class",
"snabbdom-dataset": "node_modules/snabbdom/dist/snabbdom-dataset",
"snabbdom-eventlisteners": "node_modules/snabbdom/dist/snabbdom-eventlisteners",
"snabbdom-props": "node_modules/snabbdom/dist/snabbdom-props",
"snabbdom-style": "node_modules/snabbdom/dist/snabbdom-style",
"strophe": "node_modules/strophe.js/strophe",
"strophe.ping": "node_modules/strophejs-plugin-ping/strophe.ping",
"strophe.rsm": "node_modules/strophejs-plugin-rsm/strophe.rsm",
"text": "node_modules/text/text",
"tovnode": "node_modules/snabbdom/dist/tovnode",
"tpl": "node_modules/lodash-template-loader/loader",
"underscore": "src/underscore-shim",
"uri": "node_modules/urijs/src/URI",
"utils": "src/utils/core",
"vdom-parser": "node_modules/vdom-parser/dist",
"xss": "node_modules/xss/dist/xss",
"xss.noconflict": "src/xss.noconflict",
// OMEMO/libsignal requirements
"Long": "3rdparty/long",
"protobuf": "3rdparty/protobuf",
"bytebuffer": "3rdparty/bytebuffer",
"libsignal": "3rdparty/libsignal-protocol-javascript/dist/libsignal-protocol",
// Converse
"converse": "src/converse",
"converse-bookmarks": "src/converse-bookmarks",
"converse-caps": "src/converse-caps",
"converse-chatboxes": "src/converse-chatboxes",
"converse-chatview": "src/converse-chatview",
"converse-controlbox": "src/converse-controlbox",
"converse-core": "src/converse-core",
"converse-disco": "src/converse-disco",
"converse-dragresize": "src/converse-dragresize",
"converse-embedded": "src/converse-embedded",
"converse-fullscreen": "src/converse-fullscreen",
"converse-headline": "src/converse-headline",
"converse-mam": "src/converse-mam",
"converse-message-view": "src/converse-message-view",
"converse-minimize": "src/converse-minimize",
"converse-modal": "src/converse-modal",
"converse-muc": "src/converse-muc",
"converse-muc-views": "src/converse-muc-views",
"converse-notification": "src/converse-notification",
"converse-omemo": "src/converse-omemo",
"converse-otr": "src/converse-otr",
"converse-ping": "src/converse-ping",
"converse-profile": "src/converse-profile",
"converse-register": "src/converse-register",
"converse-roomslist": "src/converse-roomslist",
"converse-roster": "src/converse-roster",
"converse-rosterview": "src/converse-rosterview",
"converse-singleton": "src/converse-singleton",
"converse-vcard": "src/converse-vcard",
// Off-the-record-encryption
// "bigint": "node_modules/otr/build/dep/bigint",
"bigint": "3rdparty/bigint",
"crypto": "node_modules/otr/build/dep/crypto",
"salsa20": "node_modules/otr/build/dep/salsa20",
"otr": "node_modules/otr/build/otr",
},
packages: [{
'name': 'moment',
'location': 'node_modules/moment',
'main': 'moment'
}],
map: {
// '*' means all modules will get the '*.noconflict' version
// as their dependency.
'*': {
'backbone': 'backbone.noconflict',
'lodash': 'lodash.noconflict'
},
// '*.noconflict' wants the real module
// If this line was not here, there would
// be an unresolvable cyclic dependency.
'backbone.noconflict': { 'backbone': 'backbone' },
'lodash.noconflict': { 'lodash': 'lodash' }
},
lodashLoader: {
// Configuration for requirejs-tpl
// Use Mustache style syntax for variable interpolation
root: "src/templates/",
templateSettings: {
"escape": /\{\{\{([\s\S]+?)\}\}\}/g,
"evaluate": /\{\[([\s\S]+?)\]\}/g,
"interpolate": /\{\{([\s\S]+?)\}\}/g,
// By default, template places the values from your data in the
// local scope via the with statement. However, you can specify
// a single variable name with the variable setting. This can
// significantly improve the speed at which a template is able
// to render.
"variable": 'o'
}
},
// define module dependencies for modules not using define
shim: {
'backbone.orderedlistview': { deps: ['backbone.nativeview'] },
'backbone.overview': { deps: ['backbone.nativeview'] },
'backbone.vdomview': { deps: ['backbone.nativeview'] },
'awesomplete': { exports: 'Awesomplete'},
'emojione': { exports: 'emojione'},
'xss': {
'init': function (xss_noconflict) {
return {
filterXSS: window.filterXSS,
filterCSS: window.filterCSS
}
}
}
}
});

View File

@ -12,10 +12,10 @@
(function (root, factory) {
define(["converse-core",
"converse-muc",
"tpl!chatroom_bookmark_form",
"tpl!chatroom_bookmark_toggle",
"tpl!bookmark",
"tpl!bookmarks_list"
"templates/chatroom_bookmark_form.html",
"templates/chatroom_bookmark_toggle.html",
"templates/bookmark.html",
"templates/bookmarks_list.html"
],
factory);
}(this, function (
@ -337,10 +337,9 @@
'type': 'get',
}).c('pubsub', {'xmlns': Strophe.NS.PUBSUB})
.c('items', {'node': 'storage:bookmarks'});
_converse.connection.sendIQ(
stanza,
_.bind(this.onBookmarksReceived, this, deferred),
_.bind(this.onBookmarksReceivedError, this, deferred)
_converse.api.sendIQ(stanza)
.then((iq) => this.onBookmarksReceived(deferred, iq))
.catch((iq) => this.onBookmarksReceivedError(deferred, iq)
);
},

View File

@ -9,9 +9,9 @@
"converse-core",
"emojione",
"filesize",
"tpl!chatboxes",
"templates/chatboxes.html",
"backbone.overview",
"form-utils"
"utils/form"
], factory);
}(this, function (converse, emojione, filesize, tpl_chatboxes) {
"use strict";
@ -29,18 +29,6 @@
// 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 (reconnecting) {
const { _converse } = this.__super__;
if (!reconnecting) {
@ -62,7 +50,9 @@
// Refer to docs/source/configuration.rst for explanations of these
// configuration settings.
_converse.api.settings.update({
auto_join_private_chats: [],
'filter_by_resource': false,
'auto_join_private_chats': [],
'forward_messages': false,
});
_converse.api.promises.add([
'chatBoxesFetched',
@ -110,17 +100,35 @@
}
},
getVCardForChatroomOccupant () {
const chatbox = this.collection.chatbox,
nick = Strophe.getResourceFromJid(this.get('from'));
if (chatbox.get('nick') === nick) {
return _converse.xmppstatus.vcard;
} else {
let vcard;
if (this.get('vcard_jid')) {
vcard = _converse.vcards.findWhere({'jid': this.get('vcard_jid')});
}
if (!vcard) {
let jid;
const occupant = chatbox.occupants.findWhere({'nick': nick});
if (occupant && occupant.get('jid')) {
jid = occupant.get('jid');
this.save({'vcard_jid': jid}, {'silent': true});
} else {
jid = this.get('from');
}
vcard = _converse.vcards.findWhere({'jid': jid}) || _converse.vcards.create({'jid': jid});
}
return vcard;
}
},
setVCard () {
if (this.get('type') === 'groupchat') {
const chatbox = this.collection.chatbox,
nick = Strophe.getResourceFromJid(this.get('from'));
if (chatbox.get('nick') === nick) {
this.vcard = _converse.xmppstatus.vcard;
} else {
const occupant = chatbox.occupants.findWhere({'nick': nick});
const jid = (occupant && occupant.get('jid')) ? occupant.get('jid') : this.get('from');
this.vcard = _converse.vcards.findWhere({'jid': jid}) || _converse.vcards.create({'jid': jid});
}
this.vcard = this.getVCardForChatroomOccupant();
} else {
const jid = this.get('from');
this.vcard = _converse.vcards.findWhere({'jid': jid}) || _converse.vcards.create({'jid': jid});
@ -231,13 +239,16 @@
_converse.ChatBox = _converse.ModelWithVCardAndPresence.extend({
defaults: {
'bookmarked': false,
'chat_state': undefined,
'num_unread': 0,
'type': 'chatbox',
'message_type': 'chat',
'url': ''
defaults () {
return {
'bookmarked': false,
'chat_state': undefined,
'num_unread': 0,
'type': 'chatbox',
'message_type': 'chat',
'url': '',
'hidden': _.includes(['mobile', 'fullscreen'], _converse.view_mode)
}
},
initialize () {
@ -401,12 +412,17 @@
getMessageBody (message) {
const type = message.getAttribute('type');
return (type === 'error') ?
_.propertyOf(message.querySelector('error text'))('textContent') :
_.propertyOf(message.querySelector('body'))('textContent');
if (type === 'error') {
const error = message.querySelector('error');
return _.propertyOf(error.querySelector('text'))('textContent') ||
__('Sorry, an error occured:') + ' ' + error.innerHTML;
} else {
return _.propertyOf(message.querySelector('body'))('textContent');
}
},
getMessageAttributesFromStanza (message, delay, original_stanza) {
getMessageAttributesFromStanza (message, original_stanza) {
/* Parses a passed in message stanza and returns an object
* of attributes.
*
@ -418,11 +434,11 @@
* that contains the message stanza, if it was
* contained, otherwise it's the message stanza itself.
*/
delay = delay || message.querySelector('delay');
const { _converse } = this.__super__,
{ __ } = _converse,
spoiler = message.querySelector(`spoiler[xmlns="${Strophe.NS.SPOILER}"]`),
archive = sizzle(`result[xmlns="${Strophe.NS.MAM}"]`, original_stanza).pop(),
spoiler = sizzle(`spoiler[xmlns="${Strophe.NS.SPOILER}"]`, original_stanza).pop(),
delay = sizzle(`delay[xmlns="${Strophe.NS.DELAY}"]`, original_stanza).pop(),
chat_state = message.getElementsByTagName(_converse.COMPOSING).length && _converse.COMPOSING ||
message.getElementsByTagName(_converse.PAUSED).length && _converse.PAUSED ||
message.getElementsByTagName(_converse.INACTIVE).length && _converse.INACTIVE ||
@ -430,13 +446,14 @@
message.getElementsByTagName(_converse.GONE).length && _converse.GONE;
const attrs = {
'type': message.getAttribute('type'),
'chat_state': chat_state,
'delayed': !_.isNull(delay),
'is_archived': !_.isNil(archive),
'is_delayed': !_.isNil(delay),
'is_spoiler': !_.isNil(spoiler),
'message': this.getMessageBody(message) || undefined,
'msgid': message.getAttribute('id'),
'time': delay ? delay.getAttribute('stamp') : moment().format(),
'is_spoiler': !_.isNull(spoiler)
'type': message.getAttribute('type')
};
if (attrs.type === 'groupchat') {
attrs.from = message.getAttribute('from');
@ -466,14 +483,15 @@
return attrs;
},
createMessage (message, delay, original_stanza) {
createMessage (message, original_stanza) {
/* Create a Backbone.Message object inside this chat box
* based on the identified message stanza.
*/
const attrs = this.getMessageAttributesFromStanza.apply(this, arguments)
const attrs = this.getMessageAttributesFromStanza(message, original_stanza);
const is_csn = u.isOnlyChatStateNotification(attrs);
if (is_csn && attrs.delayed) {
// No need showing old CSNs
if (is_csn && (attrs.is_delayed || (attrs.type === 'groupchat' && Strophe.getResourceFromJid(attrs.from) == this.get('nick')))) {
// XXX: MUC leakage
// No need showing delayed or our own CSN messages
return;
} else if (!is_csn && !attrs.file && !attrs.message && !attrs.oob_url && attrs.type !== 'error') {
// TODO: handle <subject> messages (currently being done by ChatRoom)
@ -483,7 +501,7 @@
}
},
newMessageWillBeHidden () {
isHidden () {
/* Returns a boolean to indicate whether a newly received
* message will be visible to the user or not.
*/
@ -500,7 +518,7 @@
if (_.isNull(stanza.querySelector('body'))) {
return; // The message has no text
}
if (utils.isNewMessage(stanza) && this.newMessageWillBeHidden()) {
if (utils.isNewMessage(stanza) && this.isHidden()) {
this.save({'num_unread': this.get('num_unread') + 1});
_converse.incrementMsgCounter();
}
@ -524,12 +542,14 @@
},
registerMessageHandler () {
_converse.connection.addHandler(
this.onMessage.bind(this), null, 'message', 'chat'
);
_converse.connection.addHandler(
this.onErrorMessage.bind(this), null, 'message', 'error'
);
_converse.connection.addHandler((stanza) => {
this.onMessage(stanza);
return true;
}, null, 'message', 'chat');
_converse.connection.addHandler((stanza) => {
this.onErrorMessage(stanza);
return true;
}, null, 'message', 'error');
},
chatBoxMayBeShown (chatbox) {
@ -547,29 +567,27 @@
},
onConnected () {
this.browserStorage = new Backbone.BrowserStorage[_converse.storage](
this.browserStorage = new Backbone.BrowserStorage.session(
b64_sha1(`converse.chatboxes-${_converse.bare_jid}`));
this.registerMessageHandler();
this.fetch({
add: true,
success: this.onChatBoxesFetched.bind(this)
'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);
chatbox.createMessage(message, message);
return true;
},
@ -580,8 +598,7 @@
* Parameters:
* (XMLElement) message - The incoming message stanza
*/
let contact_jid, delay, resource,
from_jid = message.getAttribute('from'),
let from_jid = message.getAttribute('from'),
to_jid = message.getAttribute('to');
const original_stanza = message,
@ -610,12 +627,10 @@
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');
}
@ -624,13 +639,12 @@
from_resource = Strophe.getResourceFromJid(from_jid),
is_me = from_bare_jid === _converse.bare_jid;
let contact_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 attrs = {
@ -645,7 +659,7 @@
// Only create the message when we're sure it's not a
// duplicate
chatbox.incrementUnreadMsgCounter(original_stanza);
chatbox.createMessage(message, delay, original_stanza);
chatbox.createMessage(message, original_stanza);
}
}
_converse.emit('message', {'stanza': original_stanza, 'chatbox': chatbox});
@ -700,9 +714,6 @@
_converse.root.appendChild(el);
}
}
if (_.includes(['mobile', 'fullscreen'], _converse.view_mode)) {
el.classList.add('fullscreen');
}
el.innerHTML = '';
this.setElement(el, false);
} else {
@ -814,12 +825,11 @@
_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;
_converse.api.listen.on('clearSession', () => {
_converse.chatboxviews.closeAllChatBoxes();
});
_converse.api.listen.on('statusInitialized', () => _converse.chatboxes.onConnected());
_converse.api.listen.on('presencesInitialized', () => _converse.chatboxes.onConnected());
/************************ END Event Handlers ************************/

View File

@ -5,26 +5,25 @@
// Licensed under the Mozilla Public License (MPLv2)
(function (root, factory) {
define([
"converse-core",
define(["converse-core",
"bootstrap",
"emojione",
"xss",
"tpl!action",
"tpl!chatbox",
"tpl!chatbox_head",
"tpl!chatbox_message_form",
"tpl!emojis",
"tpl!error_message",
"tpl!help_message",
"tpl!info",
"tpl!new_day",
"tpl!user_details_modal",
"tpl!toolbar_fileupload",
"tpl!spinner",
"tpl!spoiler_button",
"tpl!status_message",
"tpl!toolbar",
"templates/action.html",
"templates/chatbox.html",
"templates/chatbox_head.html",
"templates/chatbox_message_form.html",
"templates/emojis.html",
"templates/error_message.html",
"templates/help_message.html",
"templates/info.html",
"templates/new_day.html",
"templates/user_details_modal.html",
"templates/toolbar_fileupload.html",
"templates/spinner.html",
"templates/spoiler_button.html",
"templates/status_message.html",
"templates/toolbar.html",
"converse-modal",
"converse-chatboxes",
"converse-message-view"
@ -101,10 +100,11 @@
{ __ } = _converse;
_converse.api.settings.update({
'use_emojione': false,
'emojione_image_path': emojione.imagePathPNG,
'show_send_button': false,
'show_toolbar': true,
'time_format': 'HH:mm',
'use_emojione': false,
'visible_toolbar_buttons': {
'call': false,
'clear': true,
@ -151,6 +151,7 @@
return tpl_emojis(
_.extend(
this.model.toJSON(), {
'_': _,
'transform': _converse.use_emojione ? emojione.shortnameToImage : emojione.shortnameToUnicode,
'emojis_by_category': u.getEmojisByCategory(_converse, emojione),
'toned_emojis': u.getTonedEmojis(_converse),
@ -320,16 +321,17 @@
events: {
'change input.fileupload': 'onFileSelection',
'click .chatbox-navback': 'showControlBox',
'click .close-chatbox-button': 'close',
'click .show-user-details-modal': 'showUserDetailsModal',
'click .new-msgs-indicator': 'viewUnreadMessages',
'click .send-button': 'onFormSubmitted',
'click .show-user-details-modal': 'showUserDetailsModal',
'click .spoiler-toggle': 'toggleSpoilerMessage',
'click .toggle-call': 'toggleCall',
'click .toggle-clear': 'clearMessages',
'click .toggle-compose-spoiler': 'toggleComposeSpoilerMessage',
'click .toggle-smiley ul.emoji-picker li': 'insertEmoji',
'click .toggle-smiley': 'toggleEmojiMenu',
'click .spoiler-toggle': 'toggleSpoilerMessage',
'click .upload-file': 'toggleFileUpload',
'keypress .chat-textarea': 'keyPressed',
'input .chat-textarea': 'inputChanged'
@ -413,7 +415,15 @@
this.renderToolbar();
},
showControlBox () {
// Used in mobile view, to navigate back to the controlbox
const view = _converse.chatboxviews.get('controlbox');
view.show();
this.hide();
},
showUserDetailsModal (ev) {
ev.preventDefault();
if (_.isUndefined(this.user_details_modal)) {
this.user_details_modal = new _converse.UserDetailsModal({model: this.model});
}
@ -901,7 +911,7 @@
keyPressed (ev) {
/* Event handler for when a key is pressed in a chat box textarea.
*/
if (ev.keyCode === KEY.ENTER) {
if (ev.keyCode === KEY.ENTER && !ev.shiftKey) {
this.onFormSubmitted(ev);
} else if (ev.keyCode !== KEY.FORWARD_SLASH && this.model.get('chat_state') !== _converse.COMPOSING) {
// Set chat state to composing if keyCode is not a forward-slash
@ -1075,10 +1085,7 @@
},
afterShown () {
if (u.isPersistableModel(this.model)) {
this.model.clearUnreadMsgCounter();
this.model.save();
}
this.model.clearUnreadMsgCounter();
this.setChatState(_converse.ACTIVE);
this.renderEmojiPicker();
this.scrollDown();
@ -1155,7 +1162,7 @@
},
onWindowStateChanged (state) {
if (this.model.get('num_unread', 0) && !this.model.newMessageWillBeHidden()) {
if (this.model.get('num_unread', 0) && !this.model.isHidden()) {
this.model.clearUnreadMsgCounter();
}
}

View File

@ -10,10 +10,10 @@
define(["converse-core",
"bootstrap",
"lodash.fp",
"tpl!converse_brand_heading",
"tpl!controlbox",
"tpl!controlbox_toggle",
"tpl!login_panel",
"templates/converse_brand_heading.html",
"templates/controlbox.html",
"templates/controlbox_toggle.html",
"templates/login_panel.html",
"converse-chatview",
"converse-rosterview",
"converse-profile"
@ -91,8 +91,8 @@
//
// New functions which don't exist yet can also be added.
_tearDown () {
this.__super__._tearDown.apply(this, arguments);
tearDown () {
this.__super__.tearDown.apply(this, arguments);
if (this.rosterview) {
// Removes roster groups
this.rosterview.model.off().reset();

View File

@ -1,7 +1,7 @@
// Converse.js
// https://conversejs.org
//
// Copyright (c) 2012-2018, the Converse.js developers
// Copyright (c) 2013-2018, the Converse.js developers
// Licensed under the Mozilla Public License (MPLv2)
(function (root, factory) {
@ -11,7 +11,7 @@
"lodash.fp",
"polyfill",
"i18n",
"utils",
"utils/core",
"moment",
"strophe",
"pluggable",
@ -20,11 +20,7 @@
"backbone.browserStorage"
], factory);
}(this, function (sizzle, Promise, _, f, polyfill, i18n, u, moment, Strophe, pluggable, Backbone) {
/* Cannot use this due to Safari bug.
* See https://github.com/jcbrand/converse.js/issues/196
*/
// "use strict";
"use strict";
// Strophe globals
const { $build, $iq, $msg, $pres } = Strophe;
@ -72,9 +68,9 @@
// Core plugins are whitelisted automatically
_converse.core_plugins = [
'converse-bookmarks',
'converse-caps',
'converse-chatboxes',
'converse-chatview',
'converse-caps',
'converse-controlbox',
'converse-core',
'converse-disco',
@ -90,8 +86,10 @@
'converse-muc-views',
'converse-notification',
'converse-omemo',
'converse-oauth',
'converse-ping',
'converse-profile',
'converse-push',
'converse-register',
'converse-roomslist',
'converse-roster',
@ -153,6 +151,64 @@
_converse.DEFAULT_IMAGE_TYPE = 'image/png';
_converse.DEFAULT_IMAGE = "iVBORw0KGgoAAAANSUhEUgAAAGAAAABgCAIAAABt+uBvAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3gwHCy455JBsggAABkJJREFUeNrtnM1PE1sUwHvvTD8otWLHST/Gimi1CEgr6M6FEWuIBo2pujDVsNDEP8GN/4MbN7oxrlipG2OCgZgYlxAbkRYw1KqkIDRCSkM7nXvvW8x7vjyNeQ9m7p1p3z1LQk/v/Dhz7vkEXL161cHl9wI5Ag6IA+KAOCAOiAPigDggLhwQB2S+iNZ+PcYY/SWEEP2HAAAIoSAIoihCCP+ngDDGtVotGAz29/cfOXJEUZSOjg6n06lp2sbGRqlUWlhYyGazS0tLbrdbEASrzgksyeYJId3d3el0uqenRxRFAAAA4KdfIIRgjD9+/Pj8+fOpqSndslofEIQwHA6Pjo4mEon//qmFhYXHjx8vLi4ihBgDEnp7e9l8E0Jo165dQ0NDd+/eDYVC2/qsJElDQ0OEkKWlpa2tLZamxAhQo9EIBoOjo6MXL17csZLe3l5FUT59+lQul5l5JRaAVFWNRqN37tw5ceKEQVWRSOTw4cOFQuHbt2+iKLYCIISQLMu3b99OJpOmKAwEAgcPHszn8+vr6wzsiG6UQQhxuVyXLl0aGBgwUW0sFstkMl6v90fo1KyAMMYDAwPnzp0zXfPg4GAqlWo0Gk0MiBAiy/L58+edTqf5Aa4onj59OhaLYYybFRCEMBaL0fNxBw4cSCQStN0QRUBut3t4eJjq6U+dOiVJElVPRBFQIBDo6+ujCqirqyscDlONGykC2lYyYSR6pBoQQapHZwAoHo/TuARYAOrs7GQASFEUqn6aIiBJkhgA6ujooFpUo6iaTa7koFwnaoWadLNe81tbWwzoaJrWrICWl5cZAFpbW6OabVAEtLi4yABQsVjUNK0pAWWzWQaAcrlcswKanZ1VVZUqHYRQEwOq1Wpv3ryhCmh6erpcLjdrNl+v1ycnJ+l5UELI27dvv3//3qxxEADgy5cvExMT9Mznw4cPtFtAdAPFarU6Pj5eKpVM17yxsfHy5cvV1VXazXu62gVBKBQKT58+rdVqJqrFGL948eLdu3dU8/g/H4FBUaJYLAqC0NPTY9brMD4+PjY25mDSracOCABACJmZmXE6nUePHjWu8NWrV48ePSKEsGlAs7Agfd5nenq6Wq0mk0kjDzY2NvbkyRMIIbP2PLvhBUEQ8vl8NpuNx+M+n29bzhVjvLKycv/+/YmJCcazQuwA6YzW1tYmJyf1SY+2trZ/rRk1Go1SqfT69esHDx4UCgVmNaa/zZ/9ABUhRFXVYDB48uTJeDweiUQkSfL7/T9MA2NcqVTK5fLy8vL8/PzU1FSxWHS5XJaM4wGr9sUwxqqqer3eUCgkSZJuUBBCfTRvc3OzXC6vrKxUKhWn02nhCJ5lM4oQQo/HgxD6+vXr58+fHf8sDOp+HQDg8XgclorFU676dKLlo6yWRdItIBwQB8QBcUCtfosRQjRNQwhhjPUC4w46WXryBSHU1zgEQWBz99EFhDGu1+t+v//48ePxeFxRlD179ng8nh0Efgiher2+vr6ur3HMzMysrq7uTJVdACGEurq6Ll++nEgkPB7Pj9jPoDHqOxyqqubz+WfPnuVyuV9XPeyeagAAAoHArVu3BgcHab8CuVzu4cOHpVKJUnfA5GweY+xyuc6cOXPv3r1IJMLAR8iyPDw8XK/Xi8Wiqqqmm5KZgBBC7e3tN27cuHbtGuPVpf7+/lAoNDs7W61WzfVKpgHSSzw3b95MpVKW3MfRaDQSiczNzVUqFRMZmQOIEOL1eq9fv3727FlL1t50URRFluX5+flqtWpWEGAOIFEUU6nUlStXLKSjy759+xwOx9zcnKZpphzGHMzhcDiTydgk9r1w4YIp7RPTAAmCkMlk2FeLf/tIEKbTab/fbwtAhJBoNGrutpNx6e7uPnTokC1eMU3T0um0DZPMkZER6wERQnw+n/FFSxpy7Nix3bt3WwwIIcRgIWnHkkwmjecfRgGx7DtuV/r6+iwGhDHev3+/bQF1dnYaH6E2CkiWZdsC2rt3r8WAHA5HW1ubbQGZcjajgOwTH/4qNko1Wlg4IA6IA+KAOKBWBUQIsfNojyliKIoRRfH9+/dut9umf3wzpoUNNQ4BAJubmwz+ic+OxefzWWlBhJD29nbug7iT5sIBcUAcEAfEAXFAHBAHxOVn+QMrmWpuPZx12gAAAABJRU5ErkJggg==";
_converse.TIMEOUTS = { // Set as module attr so that we can override in tests.
'PAUSED': 10000,
'INACTIVE': 90000
};
// XEP-0085 Chat states
// http://xmpp.org/extensions/xep-0085.html
_converse.INACTIVE = 'inactive';
_converse.ACTIVE = 'active';
_converse.COMPOSING = 'composing';
_converse.PAUSED = 'paused';
_converse.GONE = 'gone';
// Default configuration values
// ----------------------------
_converse.default_settings = {
allow_non_roster_messaging: false,
animate: true,
authentication: 'login', // Available values are "login", "prebind", "anonymous" and "external".
auto_away: 0, // Seconds after which user status is set to 'away'
auto_login: false, // Currently only used in connection with anonymous login
auto_reconnect: true,
auto_xa: 0, // Seconds after which user status is set to 'xa'
blacklisted_plugins: [],
bosh_service_url: undefined,
connection_options: {},
credentials_url: null, // URL from where login credentials can be fetched
csi_waiting_time: 0, // Support for XEP-0352. Seconds before client is considered idle and CSI is sent out.
debug: false,
default_state: 'online',
expose_rid_and_sid: false,
geouri_regex: /https:\/\/www.openstreetmap.org\/.*#map=[0-9]+\/([\-0-9.]+)\/([\-0-9.]+)\S*/g,
geouri_replacement: 'https://www.openstreetmap.org/?mlat=$1&mlon=$2#map=18/$1/$2',
jid: undefined,
keepalive: true,
locales_url: 'locale/{{{locale}}}/LC_MESSAGES/converse.json',
locales: [
'af', 'ar', 'bg', 'ca', 'de', 'es', 'eu', 'en', 'fr', 'he',
'hu', 'id', 'it', 'ja', 'nb', 'nl',
'pl', 'pt_BR', 'ru', 'tr', 'uk', 'zh_CN', 'zh_TW'
],
message_carbons: true,
nickname: undefined,
password: undefined,
prebind_url: null,
priority: 0,
rid: undefined,
root: window.document,
sid: undefined,
storage: 'session',
strict_plugin_dependencies: false,
trusted: true,
view_mode: 'overlayed', // Choices are 'overlayed', 'fullscreen', 'mobile'
websocket_url: undefined,
whitelisted_plugins: []
};
_converse.log = function (message, level, style='') {
/* Logs messages to the browser's developer console.
*
@ -242,7 +298,6 @@
_converse.initialize = function (settings, callback) {
"use strict";
settings = !_.isUndefined(settings) ? settings : {};
const init_promise = u.getResolveablePromise();
@ -262,89 +317,21 @@
_converse.connection.reset();
_converse.off();
_converse.stopListening();
_converse._tearDown();
_converse.tearDown();
}
let unloadevent;
if ('onpagehide' in window) {
// Pagehide gets thrown in more cases than unload. Specifically it
// gets thrown when the page is cached and not just
// closed/destroyed. It's the only viable event on mobile Safari.
// https://www.webkit.org/blog/516/webkit-page-cache-ii-the-unload-event/
unloadevent = 'pagehide';
_converse.unloadevent = 'pagehide';
} else if ('onbeforeunload' in window) {
unloadevent = 'beforeunload';
_converse.unloadevent = 'beforeunload';
} else if ('onunload' in window) {
unloadevent = 'unload';
_converse.unloadevent = 'unload';
}
// Instance level constants
this.TIMEOUTS = { // Set as module attr so that we can override in tests.
'PAUSED': 10000,
'INACTIVE': 90000
};
// XEP-0085 Chat states
// http://xmpp.org/extensions/xep-0085.html
this.INACTIVE = 'inactive';
this.ACTIVE = 'active';
this.COMPOSING = 'composing';
this.PAUSED = 'paused';
this.GONE = 'gone';
// Default configuration values
// ----------------------------
this.default_settings = {
allow_contact_requests: true,
allow_non_roster_messaging: false,
animate: true,
authentication: 'login', // Available values are "login", "prebind", "anonymous" and "external".
auto_away: 0, // Seconds after which user status is set to 'away'
auto_login: false, // Currently only used in connection with anonymous login
auto_reconnect: true,
auto_subscribe: false,
auto_xa: 0, // Seconds after which user status is set to 'xa'
blacklisted_plugins: [],
bosh_service_url: undefined,
connection_options: {},
credentials_url: null, // URL from where login credentials can be fetched
csi_waiting_time: 0, // Support for XEP-0352. Seconds before client is considered idle and CSI is sent out.
debug: false,
default_state: 'online',
expose_rid_and_sid: false,
filter_by_resource: false,
forward_messages: false,
geouri_regex: /https:\/\/www.openstreetmap.org\/.*#map=[0-9]+\/([\-0-9.]+)\/([\-0-9.]+)\S*/g,
geouri_replacement: 'https://www.openstreetmap.org/?mlat=$1&mlon=$2#map=18/$1/$2',
hide_offline_users: false,
include_offline_state: false,
jid: undefined,
keepalive: true,
locales_url: 'locale/{{{locale}}}/LC_MESSAGES/converse.json',
locales: [
'af', 'ar', 'bg', 'ca', 'de', 'es', 'eu', 'en', 'fr', 'he',
'hu', 'id', 'it', 'ja', 'nb', 'nl',
'pl', 'pt_BR', 'ru', 'tr', 'uk', 'zh_CN', 'zh_TW'
],
message_carbons: true,
nickname: undefined,
password: undefined,
prebind_url: null,
priority: 0,
registration_domain: '',
rid: undefined,
root: window.document,
show_only_online_users: false,
show_send_button: false,
sid: undefined,
storage: 'session',
strict_plugin_dependencies: false,
synchronize_availability: true,
trusted: true,
view_mode: 'overlayed', // Choices are 'overlayed', 'fullscreen', 'mobile'
websocket_url: undefined,
whitelisted_plugins: []
};
_.assignIn(this, this.default_settings);
// Allow only whitelisted configuration attributes to be overwritten
_.assignIn(this, _.pick(settings, _.keys(this.default_settings)));
@ -460,7 +447,8 @@
window.addEventListener('focus', _converse.onUserActivity);
window.addEventListener('keypress', _converse.onUserActivity);
window.addEventListener('mousemove', _converse.onUserActivity);
window.addEventListener(unloadevent, _converse.onUserActivity);
const options = {'once': true, 'passive': true};
window.addEventListener(_converse.unloadevent, _converse.onUserActivity, options);
_converse.everySecondTrigger = window.setInterval(_converse.onEverySecond, 1000);
};
@ -492,7 +480,7 @@
__('The connection has dropped, attempting to reconnect.')
);
_converse.connection.reconnecting = true;
_converse._tearDown();
_converse.tearDown();
_converse.logIn(null, true);
}, 3000, {'leading': true});
@ -500,7 +488,7 @@
_converse.log('DISCONNECTED');
delete _converse.connection.reconnecting;
_converse.connection.reset();
_converse._tearDown();
_converse.tearDown();
_converse.emit('disconnected');
};
@ -649,8 +637,9 @@
_converse.session = new Backbone.Model();
const id = b64_sha1('converse.bosh-session');
_converse.session.id = id; // Appears to be necessary for backbone.browserStorage
_converse.session.browserStorage = new Backbone.BrowserStorage[_converse.storage](id);
_converse.session.browserStorage = new Backbone.BrowserStorage.session(id);
_converse.session.fetch();
_converse.emit('sessionInitialized');
};
this.clearSession = function () {
@ -669,7 +658,7 @@
if (!_.isUndefined(_converse.connection)) {
_converse.connection.disconnect();
} else {
_converse._tearDown();
_converse.tearDown();
}
// Recreate all the promises
_.each(_.keys(_converse.promises), addPromise);
@ -727,6 +716,7 @@
if( document[hidden] !== undefined ) {
_.partial(_converse.saveWindowState, _, hidden)({type: document[hidden] ? "blur" : "focus"});
}
_converse.emit('registeredGlobalEventHandlers');
};
this.enableCarbons = function () {
@ -748,7 +738,7 @@
'An error occured while trying to enable message carbons.',
Strophe.LogLevel.ERROR);
} else {
this.session.save({carbons_enabled: true});
this.session.save({'carbons_enabled': true});
_converse.log('Message carbons have been enabled.');
}
}, null, "iq", null, "enablecarbons");
@ -1087,7 +1077,7 @@
};
this._tearDown = function () {
this.tearDown = function () {
/* Remove those views which are only allowed with a valid
* connection.
*/
@ -1099,7 +1089,7 @@
window.removeEventListener('focus', _converse.onUserActivity);
window.removeEventListener('keypress', _converse.onUserActivity);
window.removeEventListener('mousemove', _converse.onUserActivity);
window.removeEventListener(unloadevent, _converse.onUserActivity);
window.removeEventListener(_converse.unloadevent, _converse.onUserActivity);
window.clearInterval(_converse.everySecondTrigger);
_converse.emit('afterTearDown');
return _converse;
@ -1298,6 +1288,11 @@
'send' (stanza) {
_converse.connection.send(stanza);
},
'sendIQ' (stanza) {
return new Promise((resolve, reject) => {
_converse.connection.sendIQ(stanza, resolve, reject, _converse.IQ_TIMEOUT);
});
}
};
// The public API

View File

@ -36,24 +36,24 @@
this.waitUntilFeaturesDiscovered = utils.getResolveablePromise();
this.dataforms = new Backbone.Collection();
this.dataforms.browserStorage = new Backbone.BrowserStorage[_converse.storage](
this.dataforms.browserStorage = new Backbone.BrowserStorage.session(
b64_sha1(`converse.dataforms-{this.get('jid')}`)
);
this.features = new Backbone.Collection();
this.features.browserStorage = new Backbone.BrowserStorage[_converse.storage](
this.features.browserStorage = new Backbone.BrowserStorage.session(
b64_sha1(`converse.features-${this.get('jid')}`)
);
this.features.on('add', this.onFeatureAdded, this);
this.identities = new Backbone.Collection();
this.identities.browserStorage = new Backbone.BrowserStorage[_converse.storage](
this.identities.browserStorage = new Backbone.BrowserStorage.session(
b64_sha1(`converse.identities-${this.get('jid')}`)
);
this.fetchFeatures();
this.items = new _converse.DiscoEntities();
this.items.browserStorage = new Backbone.BrowserStorage[_converse.storage](
this.items.browserStorage = new Backbone.BrowserStorage.session(
b64_sha1(`converse.disco-items-${this.get('jid')}`)
);
this.items.fetch();
@ -125,7 +125,12 @@
},
queryInfo () {
_converse.api.disco.info(this.get('jid'), null, this.onInfo.bind(this));
_converse.api.disco.info(this.get('jid'), null)
.then((stanza) => this.onInfo(stanza))
.catch((iq) => {
this.waitUntilFeaturesDiscovered.resolve();
_converse.log(iq, Strophe.LogLevel.ERROR);
});
},
onDiscoItems (stanza) {
@ -137,7 +142,12 @@
}
const jid = item.getAttribute('jid');
if (_.isUndefined(this.items.get(jid))) {
this.items.create({'jid': jid});
const entity = _converse.disco_entities.get(jid);
if (entity) {
this.items.add(entity);
} else {
this.items.create({'jid': jid});
}
}
});
},
@ -216,12 +226,34 @@
return this;
}
function initStreamFeatures () {
_converse.stream_features = new Backbone.Collection();
_converse.stream_features.browserStorage = new Backbone.BrowserStorage.session(
b64_sha1(`converse.stream-features-${_converse.bare_jid}`)
);
_converse.stream_features.fetch({
success (collection) {
if (collection.length === 0 && _converse.connection.features) {
_.forEach(
_converse.connection.features.childNodes,
(feature) => {
_converse.stream_features.create({
'name': feature.nodeName,
'xmlns': feature.getAttribute('xmlns')
});
});
}
}
});
_converse.emit('streamFeaturesAdded');
}
function initializeDisco () {
addClientFeatures();
_converse.connection.addHandler(onDiscoInfoRequest, Strophe.NS.DISCO_INFO, 'iq', 'get', null, null);
_converse.disco_entities = new _converse.DiscoEntities();
_converse.disco_entities.browserStorage = new Backbone.BrowserStorage[_converse.storage](
_converse.disco_entities.browserStorage = new Backbone.BrowserStorage.session(
b64_sha1(`converse.disco-entities-${_converse.bare_jid}`)
);
@ -235,6 +267,7 @@
}).catch(_.partial(_converse.log, _, Strophe.LogLevel.FATAL));
}
_converse.api.listen.on('sessionInitialized', initStreamFeatures);
_converse.api.listen.on('reconnected', initializeDisco);
_converse.api.listen.on('connected', initializeDisco);
@ -290,6 +323,15 @@
* @namespace
*/
'disco': {
'stream': {
'getFeature': function (name, xmlns) {
if (_.isNil(name) || _.isNil(xmlns)) {
throw new Error("name and xmlns need to be provided when calling disco.stream.getFeature");
}
return _converse.stream_features.findWhere({'name': name, 'xmlns': xmlns});
}
},
/**
* The "own" grouping
* @namespace
@ -389,7 +431,7 @@
}
},
'info' (jid, node, callback, errback, timeout) {
'info' (jid, node) {
const attrs = {xmlns: Strophe.NS.DISCO_INFO};
if (node) {
attrs.node = node;
@ -399,7 +441,7 @@
'to':jid,
'type':'get'
}).c('query', attrs);
_converse.connection.sendIQ(info, callback, errback, timeout);
return _converse.api.sendIQ(info);
},
'items' (jid, node, callback, errback, timeout) {
@ -481,10 +523,8 @@
* (String) entity_jid - The JID of the entity which might have the identity
*/
return new Promise((resolve, reject) => {
_converse.api.waitUntil('discoInitialized').then(() => {
_converse.api.disco.entities.get(entity_jid, true)
.then((entity) => resolve(entity.getIdentity(category, type)));
})
_converse.api.disco.entities.get(entity_jid, true)
.then((entity) => resolve(entity.getIdentity(category, type)));
}).catch(_.partial(_converse.log, _, Strophe.LogLevel.FATAL));
}
}

View File

@ -8,7 +8,7 @@
(function (root, factory) {
define(["converse-core",
"tpl!dragresize",
"templates/dragresize.html",
"converse-chatview",
"converse-controlbox"
], factory);

View File

@ -8,7 +8,7 @@
(function (root, factory) {
define(["converse-core",
"tpl!inverse_brand_heading",
"templates/inverse_brand_heading.html",
"converse-chatview",
"converse-controlbox",
"converse-muc",

View File

@ -9,7 +9,7 @@
(function (root, factory) {
define([
"converse-core",
"tpl!chatbox",
"templates/chatbox.html",
"converse-chatview",
], factory);
}(this, function (converse, tpl_chatbox) {
@ -138,7 +138,7 @@
'type': 'headline',
'from': from_jid
});
chatbox.createMessage(message, undefined, message);
chatbox.createMessage(message, message);
_converse.emit('message', {'chatbox': chatbox, 'stanza': message});
}
return true;

View File

@ -11,14 +11,14 @@
(function (root, factory) {
define(["sizzle",
"converse-core",
"utils",
"converse-disco",
"strophe.rsm"
], factory);
}(this, function (sizzle, converse, utils) {
}(this, function (sizzle, converse) {
"use strict";
const CHATROOMS_TYPE = 'chatroom';
const { Promise, Strophe, $iq, _, moment } = converse.env;
const u = converse.env.utils;
const RSM_ATTRIBUTES = ['max', 'first', 'last', 'after', 'before', 'index', 'count'];
// XEP-0313 Message Archive Management
@ -128,7 +128,7 @@
//
// New functions which don't exist yet can also be added.
ChatBox: {
getMessageAttributesFromStanza (message, delay, original_stanza) {
getMessageAttributesFromStanza (message, original_stanza) {
const attrs = this.__super__.getMessageAttributesFromStanza.apply(this, arguments);
const archive_id = getMessageArchiveID(original_stanza);
if (archive_id) {
@ -153,7 +153,7 @@
*/
if (this.disable_mam) { return; }
const { _converse } = this.__super__,
most_recent_msg = utils.getMostRecentMessage(this.model);
most_recent_msg = u.getMostRecentMessage(this.model);
if (_.isNil(most_recent_msg)) {
this.fetchArchivedMessages();

View File

@ -1,7 +1,7 @@
// Converse.js
// https://conversejs.org
//
// Copyright (c) 2012-2018, the Converse.js developers
// Copyright (c) 2013-2018, the Converse.js developers
// Licensed under the Mozilla Public License (MPLv2)
(function (root, factory) {
@ -10,12 +10,12 @@
"xss",
"emojione",
"filesize",
"tpl!action",
"tpl!csn",
"tpl!file_progress",
"tpl!info",
"tpl!message",
"tpl!spoiler_message"
"templates/action.html",
"templates/csn.html",
"templates/file_progress.html",
"templates/info.html",
"templates/message.html",
"templates/spoiler_message.html"
], factory);
}(this, function (
converse,
@ -145,6 +145,7 @@
msg_content.innerHTML = _.flow(
_.partial(u.geoUriToHttp, _, _converse.geouri_replacement),
u.addHyperlinks,
u.renderNewLines,
_.partial(u.addEmoji, _converse, emojione, _)
)(text);
}
@ -225,7 +226,7 @@
},
getExtraMessageClasses () {
let extra_classes = this.model.get('delayed') && 'delayed' || '';
let extra_classes = this.model.get('is_delayed') && 'delayed' || '';
if (this.model.get('type') === 'groupchat' && this.model.get('sender') === 'them') {
if (this.model.collection.chatbox.isUserMentioned(this.model.get('message'))) {
// Add special class to mark groupchat messages

View File

@ -8,10 +8,10 @@
(function (root, factory) {
define(["converse-core",
"tpl!chatbox_minimize",
"tpl!toggle_chats",
"tpl!trimmed_chat",
"tpl!chats_panel",
"templates/chatbox_minimize.html",
"templates/toggle_chats.html",
"templates/trimmed_chat.html",
"templates/chats_panel.html",
"converse-chatview"
], factory);
}(this, function (
@ -52,16 +52,6 @@
//
// New functions which don't exist yet can also be added.
registerGlobalEventHandlers () {
const { _converse } = this.__super__;
window.addEventListener("resize", _.debounce(function (ev) {
if (_converse.connection.connected) {
_converse.chatboxviews.trimChats();
}
}, 200));
return this.__super__.registerGlobalEventHandlers.apply(this, arguments);
},
ChatBox: {
initialize () {
this.__super__.initialize.apply(this, arguments);
@ -541,6 +531,15 @@
_converse.emit('minimizedChatsInitialized');
}).catch(_.partial(_converse.log, _, Strophe.LogLevel.FATAL));
_converse.on('registeredGlobalEventHandlers', function () {
window.addEventListener("resize", _.debounce(function (ev) {
if (_converse.connection.connected) {
_converse.chatboxviews.trimChats();
}
}, 200));
});
_converse.on('controlBoxOpened', function (chatbox) {
// Wrapped in anon method because at scan time, chatboxviews
// attr not set yet.

Some files were not shown because too many files have changed in this diff Show More