Merge branch 'master' into converse-omemo

This commit is contained in:
JC Brand 2018-07-21 11:34:51 +02:00
commit bf13927946
116 changed files with 26398 additions and 22676 deletions

View File

@ -34,7 +34,7 @@
"array-bracket-spacing": "off",
"array-callback-return": "error",
"arrow-body-style": "off",
"arrow-parens": "error",
"arrow-parens": "off",
"arrow-spacing": "error",
"block-scoped-var": "off",
"block-spacing": "off",

View File

@ -7,6 +7,7 @@
- #161 XEP-0363: HTTP File Upload
- #194 Include entity capabilities in outgoing presence stanzas
- #337 API call to update a VCard
- #421 XEP-0308: Last Message Correction
- #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

View File

@ -5,7 +5,7 @@
[![Bountysource bounties](https://img.shields.io/bountysource/team/converse.js/activity.svg?maxAge=2592000)](https://www.bountysource.com/teams/converse.js/issues?tracker_ids=194169)
[![Translation status](https://hosted.weblate.org/widgets/conversejs/-/svg-badge.svg)](https://hosted.weblate.org/engage/conversejs/?utm_source=widget)
[Converse.js](https://conversejs.org) is a web based [XMPP/Jabber](http://xmpp.org) instant messaging client.
[Converse.js](https://conversejs.org) is a web based [XMPP/Jabber](https://xmpp.org) instant messaging client.
It enables you to add chat functionality to your website, independent of
any specific backend. You will however need an XMPP server to connect
@ -23,13 +23,13 @@ avialable at [https://conversejs.org/demo/embedded.html](https://conversejs.org/
### Converse.js: As seen on the conversejs.org website
![Screenshot of Converse.js](https://opkode.com/img/converse-screenshot.png)
![Screenshot of Converse](https://opkode.com/img/converse-screenshot.png)
### inVerse: a fullscreen version of converse.js
Converse.js is also available in a fullscreen version, called [inVerse](https://inverse.chat)
Converse.js is also available in a fullscreen version, hosted at [inverse.chat](https://inverse.chat)
![Screenshot of inVerse](https://opkode.com/img/inverse-screenshot.png)
![Screenshot of Converse in fullscreen mode](https://opkode.com/img/inverse-screenshot.png)
## Documentation
@ -43,45 +43,46 @@ which shows you how to use the CDN (content delivery network) to quickly get a d
- A [plugin architecture](https://conversejs.org/docs/html/plugin_development.html) based on [pluggable.js](https://conversejs.github.io/pluggable.js/)
- Single-user and group chats
- Contacts and groups
- Multi-user chat rooms [XEP 45](http://xmpp.org/extensions/xep-0045.html)
- Direct invitations to chat rooms [XEP 249](http://xmpp.org/extensions/xep-0249.html)
- vCard support [XEP 54](http://xmpp.org/extensions/xep-0054.html)
- Service discovery [XEP 30](http://xmpp.org/extensions/xep-0030.html)
- In-band registration [XEP 77](http://xmpp.org/extensions/xep-0077.html)
- Chat room bookmarks [XEP 48](http://xmpp.org/extensions/xep-0048.html)
- Roster item exchange [XEP 144](http://xmpp.org/extensions/tmp/xep-0144-1.1.html)
- Multi-user chat rooms [XEP 45](https://xmpp.org/extensions/xep-0045.html)
- Direct invitations to chat rooms [XEP 249](https://xmpp.org/extensions/xep-0249.html)
- vCard support [XEP 54](https://xmpp.org/extensions/xep-0054.html)
- Service discovery [XEP 30](https://xmpp.org/extensions/xep-0030.html)
- In-band registration [XEP 77](https://xmpp.org/extensions/xep-0077.html)
- Chat room bookmarks [XEP 48](https://xmpp.org/extensions/xep-0048.html)
- Roster item exchange [XEP 144](https://xmpp.org/extensions/tmp/xep-0144-1.1.html)
- Chat statuses (online, busy, away, offline)
- Custom status messages
- Desktop notifications
- Typing and state notifications [XEP 85](http://xmpp.org/extensions/xep-0085.html)
- Messages appear in all connnected chat clients [XEP 280](http://xmpp.org/extensions/xep-0280.html)
- Third person "/me" messages [XEP 245](http://xmpp.org/extensions/xep-0245.html)
- XMPP Ping [XEP 199](http://xmpp.org/extensions/xep-0199.html)
- Server-side archiving of messages [XEP 313](http://xmpp.org/extensions/xep-0313.html)
- Hidden Messages (aka Spoilers) [XEP 382](http://xmpp.org/extensions/xep-0382.html)
- Client state indication [XEP 352](http://xmpp.org/extensions/xep-0352.html)
- Typing and state notifications [XEP 85](https://xmpp.org/extensions/xep-0085.html)
- Messages appear in all connnected chat clients [XEP 280](https://xmpp.org/extensions/xep-0280.html)
- Third person "/me" messages [XEP 245](https://xmpp.org/extensions/xep-0245.html)
- XMPP Ping [XEP 199](https://xmpp.org/extensions/xep-0199.html)
- Server-side archiving of messages [XEP 313](https://xmpp.org/extensions/xep-0313.html)
- Hidden Messages (aka Spoilers) [XEP 382](https://xmpp.org/extensions/xep-0382.html)
- Client state indication [XEP 352](https://xmpp.org/extensions/xep-0352.html)
- Last Message Correction [XEP 308](https://xmpp.org/extensions/xep-0308.html)
- Off-the-record encryption
- Translated into 16 languages
## Integration into other frameworks
- **[Ruby on Rails](http://rubyonrails.org)**: [conversejs-rails](https://github.com/mikemarsian/conversejs-rails)
- **[Django](http://www.djangoproject.com)**: [django-conversejs](https://pypi.python.org/pypi/django-conversejs) or [django-xmpp](https://github.com/fpytloun/django-xmpp)
- **[Plone](http://plone.com)**: [collective.converse](http://github.com/collective/collective.converse)
- **[Roundcube](http://roundcube.net)**: [roundcube-converse.js-xmpp-plugin](https://github.com/devurandom/roundcube-converse.js-xmpp-plugin)
- **[Wordpress](http://wordpress.org)**: [ConverseJS](http://wordpress.org/plugins/conversejs)
- **[Ruby on Rails](https://rubyonrails.org)**: [conversejs-rails](https://github.com/mikemarsian/conversejs-rails)
- **[Django](https://www.djangoproject.com)**: [django-conversejs](https://pypi.python.org/pypi/django-conversejs) or [django-xmpp](https://github.com/fpytloun/django-xmpp)
- **[Plone](https://plone.com)**: [collective.converse](https://github.com/collective/collective.converse)
- **[Roundcube](https://roundcube.net)**: [roundcube-converse.js-xmpp-plugin](https://github.com/devurandom/roundcube-converse.js-xmpp-plugin)
- **[Wordpress](https://wordpress.org)**: [ConverseJS](https://wordpress.org/plugins/conversejs/)
- **[Patternslib](http://patternslib.com)**: [patterns.converse](https://github.com/jcbrand/patterns.converse)
- **[Alfresco](http://www.alfresco.com)**: [alfresco-js-chat-share](https://github.com/keensoft/alfresco-js-chat-share)
- **[Friendica](http://friendica.com)**: [converse](https://github.com/friendica/friendica-addons/tree/master/xmpp/converse)
- **[Tiki Wiki CMS Groupware](http://tiki.org)**: [built-in optional feature](https://doc.tiki.org/XMPP)
- **[Alfresco](https://www.alfresco.com)**: [alfresco-js-chat-share](https://github.com/keensoft/alfresco-js-chat-share)
- **[Friendica](https://friendi.ca)**: [converse](https://github.com/friendica/friendica-addons/tree/master/xmpp/converse)
- **[Tiki Wiki CMS Groupware](https://tiki.org)**: [built-in optional feature](https://doc.tiki.org/XMPP)
## Screencasts
*Note: These screencasts are already quite old! Converse.js has grown and evolved further since then.*
- [In a static HTML page](http://opkode.com/media/blog/2013/04/02/converse.js-xmpp-instant-messaging-with-javascript).
- [In a static HTML page](https://opkode.com/media/blog/2013/04/02/converse.js-xmpp-instant-messaging-with-javascript).
Here we chat to external XMPP accounts on Jabber.org and Gmail.
- [Integrated into a Plone site](http://opkode.com/media/blog/instant-messaging-for-plone-with-javascript-and-xmpp)
- [Integrated into a Plone site](https://opkode.com/media/blog/instant-messaging-for-plone-with-javascript-and-xmpp)
via collective.xmpp.chat.
- [Off-the-record encryption](https://opkode.com/media/blog/2013/11/11/conversejs-otr-support)
in Converse 0.7.
@ -123,4 +124,4 @@ The following people are making recurring donations:
Additionally this project is supported by
* [![KeyCDN](https://conversejs.org/logo/keycdn.png)](https://www.keycdn.com/)
* [![Wikisuite](https://conversejs.org/logo/wikisuite.png)](http://wikisuite.org)
* [![Wikisuite](https://conversejs.org/logo/wikisuite.png)](https://wikisuite.org)

View File

@ -6872,7 +6872,7 @@ body.reset {
flex-direction: row-reverse; }
#conversejs.converse-fullscreen .converse-chatboxes, #conversejs.converse-mobile .converse-chatboxes {
width: 100vw;
right: 15px; }
left: -15px; }
#conversejs.converse-overlayed {
height: 3em; }
#conversejs .brand-heading {
@ -7346,9 +7346,8 @@ body.reset {
max-width: 25%;
padding: 0; }
#conversejs .chat-head .user-custom-message {
color: white;
color: #e7f7ee;
font-size: 75%;
font-style: italic;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
@ -7389,7 +7388,7 @@ body.reset {
background-color: #3AA569;
box-shadow: 1px 3px 5px 3px rgba(0, 0, 0, 0.4);
z-index: 1;
overflow-y: scroll;
overflow-y: hidden;
width: 100%; }
@media screen and (max-height: 450px) {
#conversejs .chatbox .box-flyout {
@ -7656,18 +7655,6 @@ body.reset {
#conversejs.converse-overlayed .chatbox .box-flyout {
min-width: 250px !important;
width: 250px; }
#conversejs.converse-embedded .chatbox .chat-body .chat-message,
#conversejs.converse-overlayed .chatbox .chat-body .chat-message {
line-height: 20px; }
#conversejs.converse-embedded .chatbox .chat-body .chat-message .chat-msg-author,
#conversejs.converse-overlayed .chatbox .chat-body .chat-message .chat-msg-author {
line-height: 20px; }
#conversejs.converse-embedded .chatbox .chat-body .chat-message .chat-msg-content,
#conversejs.converse-overlayed .chatbox .chat-body .chat-message .chat-msg-content {
line-height: 20px; }
#conversejs.converse-embedded .chatbox .chat-body .chat-message .chat-msg-content .emojione,
#conversejs.converse-overlayed .chatbox .chat-body .chat-message .chat-msg-content .emojione {
margin-bottom: -5px; }
#conversejs.converse-embedded .chatbox form.sendXMPPMessage .chat-toolbar li .toolbar-menu,
#conversejs.converse-overlayed .chatbox form.sendXMPPMessage .chat-toolbar li .toolbar-menu {
min-width: 235px; }
@ -7709,7 +7696,7 @@ body.reset {
font-size: 20px;
padding: 0; }
#conversejs.converse-fullscreen .chat-head .user-custom-message {
font-size: 50%;
font-size: 70%;
height: auto;
line-height: 16px; }
#conversejs.converse-fullscreen .chat-head .chatbox-title {
@ -7733,8 +7720,8 @@ body.reset {
padding-left: 15px; }
@media (min-width: 768px) {
#conversejs.converse-fullscreen .chatbox {
flex: 0 0 75%;
max-width: 75%; } }
flex: 0 0 66.6666666667%;
max-width: 66.6666666667%; } }
@media (min-width: 992px) {
#conversejs.converse-fullscreen .chatbox {
flex: 0 0 75%;
@ -7748,21 +7735,12 @@ body.reset {
box-shadow: none;
height: 100vh;
min-height: 50vh;
width: 100%; }
width: 100%;
overflow: hidden; }
#conversejs.converse-fullscreen .chatbox .chat-body {
background-color: #3AA569;
border-top-left-radius: 4px;
border-top-right-radius: 4px; }
#conversejs.converse-fullscreen .chatbox .chat-body .chat-message {
line-height: 16px;
font-size: 12px; }
#conversejs.converse-fullscreen .chatbox .chat-body .chat-message .chat-msg-author {
line-height: 16px; }
#conversejs.converse-fullscreen .chatbox .chat-body .chat-message .chat-msg-content {
line-height: 16px; }
#conversejs.converse-fullscreen .chatbox .chat-body .chat-message .chat-msg-content .emojione {
height: 16px;
margin-bottom: -4px; }
#conversejs.converse-fullscreen .chatbox .chat-content {
border-top-left-radius: 4px;
border-top-right-radius: 4px; }
@ -7828,15 +7806,24 @@ body.reset {
color: #578EA9;
font-size: 20px;
margin-right: 0.5em; }
#conversejs .set-xmpp-status .fa-circle, #conversejs .xmpp-status .fa-circle, #conversejs .roster-contacts .fa-circle {
#conversejs .set-xmpp-status .fa-circle,
#conversejs .xmpp-status .fa-circle,
#conversejs .roster-contacts .fa-circle {
color: #3AA569; }
#conversejs .set-xmpp-status .fa-minus-circle, #conversejs .xmpp-status .fa-minus-circle, #conversejs .roster-contacts .fa-minus-circle {
#conversejs .set-xmpp-status .fa-minus-circle,
#conversejs .xmpp-status .fa-minus-circle,
#conversejs .roster-contacts .fa-minus-circle {
color: #E77051; }
#conversejs .set-xmpp-status .fa-dot-circle-o, #conversejs .xmpp-status .fa-dot-circle-o, #conversejs .roster-contacts .fa-dot-circle-o {
#conversejs .set-xmpp-status .fa-dot-circle-o,
#conversejs .xmpp-status .fa-dot-circle-o,
#conversejs .roster-contacts .fa-dot-circle-o {
color: #E7A151; }
#conversejs .set-xmpp-status .fa-circle-o, #conversejs .xmpp-status .fa-circle-o, #conversejs .roster-contacts .fa-circle-o {
color: #A8ABA1; }
#conversejs .set-xmpp-status .fa-times-circle, #conversejs .xmpp-status .fa-times-circle, #conversejs .roster-contacts .fa-times-circle {
#conversejs .set-xmpp-status .fa-circle-o,
#conversejs .set-xmpp-status .fa-times-circle,
#conversejs .xmpp-status .fa-circle-o,
#conversejs .xmpp-status .fa-times-circle,
#conversejs .roster-contacts .fa-circle-o,
#conversejs .roster-contacts .fa-times-circle {
color: #A8ABA1; }
#conversejs .room-info {
font-size: 12px;
@ -8049,37 +8036,6 @@ body.reset {
#conversejs .toggle-controlbox span {
color: white; }
@media (max-width: 767.98px) {
#conversejs:not(.converse-embedded) {
left: 0;
right: 0;
padding-left: env(safe-area-inset-left);
padding-right: env(safe-area-inset-right); }
#conversejs:not(.converse-embedded) .converse-chatboxes {
margin: 0 !important;
flex-direction: row !important;
justify-content: space-between; }
#conversejs:not(.converse-embedded) .converse-chatboxes .converse-chatroom {
font-size: 14px; }
#conversejs:not(.converse-embedded) .converse-chatboxes .chatbox .box-flyout {
margin-left: 15px;
left: 0;
bottom: 0;
border-radius: 0;
width: 100vw !important;
height: 100vh !important; }
#conversejs:not(.converse-embedded) .converse-chatboxes #controlbox {
width: 100vw !important; }
#conversejs:not(.converse-embedded) .converse-chatboxes #controlbox .box-flyout {
width: 100vw !important;
height: 100vh !important;
margin-left: 30px; }
#conversejs:not(.converse-embedded) .converse-chatboxes #controlbox .sidebar {
display: block; }
#conversejs:not(.converse-embedded) .converse-chatboxes.sidebar-open .chatbox:not(#controlbox) {
display: none; }
#conversejs:not(.converse-embedded) .converse-chatboxes.sidebar-open #controlbox .controlbox-pane {
display: block; } }
#conversejs.converse-overlayed #controlbox {
order: -1;
min-width: 250px !important;
@ -8127,8 +8083,8 @@ body.reset {
@media (min-width: 768px) {
#conversejs.converse-fullscreen #controlbox,
#conversejs.converse-mobile #controlbox {
flex: 0 0 25%;
max-width: 25%; } }
flex: 0 0 33.3333333333%;
max-width: 33.3333333333%; } }
@media (min-width: 992px) {
#conversejs.converse-fullscreen #controlbox,
#conversejs.converse-mobile #controlbox {
@ -8256,106 +8212,39 @@ body.reset {
#conversejs.converse-mobile #controlbox #converse-login input[type=button] {
width: auto; }
#conversejs .list-container {
text-align: left;
padding: 0.3em 0; }
#conversejs .list-container .rooms-toggle {
font-family: "Century Gothic", futura, "URW Gothic L", Verdana, sans-serif;
display: block;
color: #666;
padding: 0 0 0.5rem 0; }
#conversejs .list-container .rooms-toggle:hover {
color: #585B51; }
#conversejs .list-container .items-list {
text-align: left; }
#conversejs .list-container .items-list .list-item {
border: none;
clear: both;
color: #666;
display: block;
height: 2em;
overflow: hidden;
padding-top: 0.5em;
text-shadow: 0 1px 0 #FAFAFA;
word-wrap: break-word; }
#conversejs .list-container .items-list .available-chatroom:hover,
#conversejs .list-container .items-list .open-headline:hover,
#conversejs .list-container .items-list .open-chatroom:hover {
background-color: #eff4f7; }
#conversejs .list-container .items-list .available-chatroom:hover a.add-bookmark,
#conversejs .list-container .items-list .available-chatroom:hover a.room-info,
#conversejs .list-container .items-list .open-headline:hover a.add-bookmark,
#conversejs .list-container .items-list .open-headline:hover a.room-info,
#conversejs .list-container .items-list .open-chatroom:hover a.add-bookmark,
#conversejs .list-container .items-list .open-chatroom:hover a.room-info {
display: block !important; }
#conversejs .list-container .items-list .available-chatroom.unread-msgs .msgs-indicator,
#conversejs .list-container .items-list .open-headline.unread-msgs .msgs-indicator,
#conversejs .list-container .items-list .open-chatroom.unread-msgs .msgs-indicator {
border-radius: 10%;
opacity: 1; }
#conversejs .list-container .items-list .available-chatroom.unread-msgs .available-room,
#conversejs .list-container .items-list .available-chatroom.unread-msgs .open-room,
#conversejs .list-container .items-list .open-headline.unread-msgs .available-room,
#conversejs .list-container .items-list .open-headline.unread-msgs .open-room,
#conversejs .list-container .items-list .open-chatroom.unread-msgs .available-room,
#conversejs .list-container .items-list .open-chatroom.unread-msgs .open-room {
width: 100%;
font-weight: bold; }
#conversejs .list-container .items-list .available-chatroom a:hover,
#conversejs .list-container .items-list .open-headline a:hover,
#conversejs .list-container .items-list .open-chatroom a:hover {
color: #206485; }
#conversejs .list-container .items-list .available-chatroom a.add-bookmark, #conversejs .list-container .items-list .available-chatroom a.room-info,
#conversejs .list-container .items-list .open-headline a.add-bookmark,
#conversejs .list-container .items-list .open-headline a.room-info,
#conversejs .list-container .items-list .open-chatroom a.add-bookmark,
#conversejs .list-container .items-list .open-chatroom a.room-info {
@media (max-width: 767.98px) {
#conversejs:not(.converse-embedded) {
left: 0;
right: 0;
padding-left: env(safe-area-inset-left);
padding-right: env(safe-area-inset-right); }
#conversejs:not(.converse-embedded) .converse-chatboxes {
margin: 0 !important;
flex-direction: row !important;
justify-content: space-between; }
#conversejs:not(.converse-embedded) .converse-chatboxes .converse-chatroom {
font-size: 14px; }
#conversejs:not(.converse-embedded) .converse-chatboxes .chatbox .box-flyout {
margin-left: 15px;
left: 0;
bottom: 0;
border-radius: 0;
width: 100vw !important;
height: 100vh !important; }
#conversejs:not(.converse-embedded) .converse-chatboxes #controlbox {
width: 100vw !important; }
#conversejs:not(.converse-embedded) .converse-chatboxes #controlbox .box-flyout {
width: 100vw !important;
height: 100vh !important; }
#conversejs:not(.converse-embedded) .converse-chatboxes #controlbox .sidebar {
display: block; }
#conversejs:not(.converse-embedded) .converse-chatboxes.sidebar-open .chatbox:not(#controlbox) {
display: none; }
#conversejs .list-container .items-list .available-chatroom a.add-bookmark:before, #conversejs .list-container .items-list .available-chatroom a.room-info:before,
#conversejs .list-container .items-list .open-headline a.add-bookmark:before,
#conversejs .list-container .items-list .open-headline a.room-info:before,
#conversejs .list-container .items-list .open-chatroom a.add-bookmark:before,
#conversejs .list-container .items-list .open-chatroom a.room-info:before {
font-size: 15px; }
#conversejs .list-container .items-list .available-chatroom a.open-room,
#conversejs .list-container .items-list .open-headline a.open-room,
#conversejs .list-container .items-list .open-chatroom a.open-room {
width: 68%;
float: left;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
padding-right: 0.5em; }
#conversejs .list-container .items-list .available-chatroom a.available-room,
#conversejs .list-container .items-list .open-headline a.available-room,
#conversejs .list-container .items-list .open-chatroom a.available-room {
width: 85%; }
#conversejs .list-container .items-list .available-chatroom .add-bookmark,
#conversejs .list-container .items-list .available-chatroom .remove-bookmark,
#conversejs .list-container .items-list .open-headline .add-bookmark,
#conversejs .list-container .items-list .open-headline .remove-bookmark,
#conversejs .list-container .items-list .open-chatroom .add-bookmark,
#conversejs .list-container .items-list .open-chatroom .remove-bookmark {
color: #A8ABA1; }
#conversejs .list-container .items-list .available-chatroom .add-bookmark.button-on,
#conversejs .list-container .items-list .available-chatroom .remove-bookmark.button-on,
#conversejs .list-container .items-list .open-headline .add-bookmark.button-on,
#conversejs .list-container .items-list .open-headline .remove-bookmark.button-on,
#conversejs .list-container .items-list .open-chatroom .add-bookmark.button-on,
#conversejs .list-container .items-list .open-chatroom .remove-bookmark.button-on {
color: #578EA9; }
#conversejs .list-container .items-list .available-chatroom .add-bookmark.button-on:hover,
#conversejs .list-container .items-list .available-chatroom .remove-bookmark.button-on:hover,
#conversejs .list-container .items-list .open-headline .add-bookmark.button-on:hover,
#conversejs .list-container .items-list .open-headline .remove-bookmark.button-on:hover,
#conversejs .list-container .items-list .open-chatroom .add-bookmark.button-on:hover,
#conversejs .list-container .items-list .open-chatroom .remove-bookmark.button-on:hover {
color: #206485; }
#conversejs.fullscreen #controlbox #chatrooms .bookmarks-list dl.rooms-list.bookmarks dd.available-chatroom a.open-room {
width: 80%; }
#conversejs:not(.converse-embedded) .converse-chatboxes.sidebar-open #controlbox .controlbox-pane {
display: block; }
#conversejs.converse-overlayed .converse-chatboxes .chatbox .box-flyout {
margin-left: 30px; } }
#conversejs #converse-roster {
text-align: left;
width: 100%;
@ -8405,27 +8294,12 @@ body.reset {
padding-bottom: 0.3rem; }
#conversejs #converse-roster .roster-contacts .roster-group .group-toggle:hover {
color: #585B51; }
#conversejs #converse-roster .roster-contacts .roster-group li {
border: none;
clear: both;
color: #666;
display: block;
overflow-y: hidden;
text-shadow: 0 1px 0 #FAFAFA;
line-height: 14px;
width: 100%;
height: 2em;
padding-top: 0.5em; }
#conversejs #converse-roster .roster-contacts .roster-group li.requesting-xmpp-contact a {
line-height: 16px; }
#conversejs #converse-roster .roster-contacts .roster-group li.requesting-xmpp-contact a.fa {
width: 1.5em; }
#conversejs #converse-roster .roster-contacts .roster-group li.requesting-xmpp-contact .req-contact-name {
padding: 0 0.2em 0 0; }
#conversejs #converse-roster .roster-contacts .roster-group li a:hover {
color: #206485; }
#conversejs #converse-roster .roster-contacts .roster-group li a .fa:hover {
color: white; }
#conversejs #converse-roster .roster-contacts .roster-group li .open-chat {
margin: 0;
padding: 0; }
@ -8446,7 +8320,7 @@ body.reset {
text-overflow: ellipsis;
padding: 0;
margin: 0;
max-width: 80%;
max-width: 90%;
float: none;
height: 100%; }
#conversejs #converse-roster .roster-contacts .roster-group li .open-chat .contact-name.unread-msgs {
@ -8466,18 +8340,8 @@ body.reset {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis; }
#conversejs #converse-roster .roster-contacts .roster-group li span {
padding: 0; }
#conversejs #converse-roster .roster-contacts .roster-group li .decline-xmpp-request {
margin-left: 5px; }
#conversejs #converse-roster .roster-contacts .roster-group li .remove-xmpp-contact {
font-size: 10px;
margin: 0;
padding: 0;
width: 2em;
display: none; }
#conversejs #converse-roster .roster-contacts .roster-group li .remove-xmpp-contact:before {
font-size: 14px; }
#conversejs #converse-roster .roster-contacts .roster-group li:hover {
background-color: #eff4f7; }
#conversejs #converse-roster .roster-contacts .roster-group li:hover .remove-xmpp-contact {
@ -8486,6 +8350,86 @@ body.reset {
line-height: 16px;
width: 100%; }
#conversejs .list-container {
text-align: left;
padding: 0.3em 0; }
#conversejs .list-container .list-toggle {
font-family: "Century Gothic", futura, "URW Gothic L", Verdana, sans-serif;
display: block;
color: #666;
padding: 0 0 0.5rem 0; }
#conversejs .list-container .list-toggle:hover {
color: #585B51; }
#conversejs .items-list {
text-align: left; }
#conversejs .items-list .list-item {
border: none;
clear: both;
color: #666;
display: block;
height: 2em;
overflow: hidden;
padding-top: 0.5em;
text-shadow: 0 1px 0 #FAFAFA;
word-wrap: break-word; }
#conversejs .items-list .list-item .list-item-link {
font-size: 14px;
line-height: 14px;
padding-right: 0.5em;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis; }
#conversejs .items-list .list-item .list-item-link:hover {
color: #206485; }
#conversejs .items-list .list-item .list-item-action {
opacity: 0;
font-size: 10px;
padding: 0;
margin: 0 0 0 0.4em;
width: 1.6em;
color: #A8ABA1; }
#conversejs .items-list .list-item .list-item-action:before {
font-size: 14px; }
#conversejs .items-list .list-item .list-item-action.button-on {
color: #578EA9; }
#conversejs .items-list .list-item .list-item-action.button-on:hover {
color: #206485; }
#conversejs .items-list .list-item .list-item-action:hover {
color: #818479;
opacity: 1; }
#conversejs .items-list .list-item.open {
background-color: #578EA9; }
#conversejs .items-list .list-item.open:hover {
background-color: #578EA9 !important; }
#conversejs .items-list .list-item.open a {
color: white; }
#conversejs .items-list .list-item.open .list-item-link:hover {
color: white; }
#conversejs .items-list .list-item.open .list-item-action {
color: #e3eef3; }
#conversejs .items-list .list-item.open .list-item-action:hover {
color: white; }
#conversejs .items-list .list-item.open .fa-circle {
color: #89d6ab; }
#conversejs .items-list .list-item.open .fa-minus-circle {
color: #f0a794; }
#conversejs .items-list .list-item.open .fa-dot-circle-o {
color: #f0c594; }
#conversejs .items-list .list-item.open .fa-circle-o,
#conversejs .items-list .list-item.open .fa-times-circle {
color: #e6e7e4; }
#conversejs .items-list .list-item:hover {
background-color: #eff4f7; }
#conversejs .items-list .list-item:hover .fa {
opacity: 1; }
#conversejs .items-list .list-item.unread-msgs .msgs-indicator {
border-radius: 10%;
opacity: 1; }
#conversejs .items-list .list-item.unread-msgs .available-room,
#conversejs .items-list .list-item.unread-msgs .open-room {
width: 100%;
font-weight: bold; }
#conversejs.converse-embedded .add-chatroom input[type="submit"],
#conversejs.converse-embedded .add-chatroom input[type="button"],
#conversejs .add-chatroom input[type="submit"],
@ -8597,9 +8541,13 @@ body.reset {
#conversejs.converse-embedded .chatroom .box-flyout .chatroom-body .mentioned,
#conversejs .chatroom .box-flyout .chatroom-body .mentioned {
font-weight: bold; }
#conversejs.converse-embedded .chatroom .box-flyout .chatroom-body .disconnect-msg,
#conversejs .chatroom .box-flyout .chatroom-body .disconnect-msg {
padding: 2em 2em 0 2em; }
#conversejs.converse-embedded .chatroom .box-flyout .chatroom-body .disconnect-container,
#conversejs .chatroom .box-flyout .chatroom-body .disconnect-container {
margin: 1em;
width: 100%; }
#conversejs.converse-embedded .chatroom .box-flyout .chatroom-body .disconnect-container h3.disconnect-msg,
#conversejs .chatroom .box-flyout .chatroom-body .disconnect-container h3.disconnect-msg {
padding-bottom: 1em; }
#conversejs.converse-embedded .chatroom .box-flyout .chatroom-body .chat-area,
#conversejs .chatroom .box-flyout .chatroom-body .chat-area {
display: flex;
@ -8715,11 +8663,6 @@ body.reset {
#conversejs .chatroom .box-flyout .chatroom-body .chatroom-form-container .validation-message {
font-size: 90%;
color: #A53214; }
#conversejs.converse-embedded .chatroom .box-flyout .chatroom-body .chatroom-form-container .chatroom-form label,
#conversejs.converse-embedded .chatroom .box-flyout .chatroom-body .chatroom-form-container .chatroom-form input[type=text],
#conversejs .chatroom .box-flyout .chatroom-body .chatroom-form-container .chatroom-form label,
#conversejs .chatroom .box-flyout .chatroom-body .chatroom-form-container .chatroom-form input[type=text] {
display: block; }
#conversejs.converse-embedded .chatroom .box-flyout .chatroom-body .chatroom-form-container input[type=button],
#conversejs.converse-embedded .chatroom .box-flyout .chatroom-body .chatroom-form-container input[type=submit],
#conversejs .chatroom .box-flyout .chatroom-body .chatroom-form-container input[type=button],
@ -8889,7 +8832,7 @@ body.reset {
width: auto;
max-height: 15em;
max-width: 100%; }
#conversejs .message.chat-action {
#conversejs .message.chat-msg--action {
font-style: italic; }
#conversejs .message.chat-msg {
display: flex;
@ -8901,6 +8844,12 @@ body.reset {
-webkit-animation: colorchange-chatmessage 1s; }
#conversejs .message.chat-msg:hover {
background-color: rgba(0, 0, 0, 0.035); }
#conversejs .message.chat-msg:hover .chat-msg__actions .chat-msg__action {
opacity: 1; }
#conversejs .message.chat-msg.correcting.groupchat {
background-color: #fdf1ee; }
#conversejs .message.chat-msg.correcting:not(.groupchat) {
background-color: #e7f7ee; }
#conversejs .message.chat-msg .spoiler {
margin-top: 0.5em; }
#conversejs .message.chat-msg .spoiler-hint {
@ -8913,54 +8862,94 @@ body.reset {
#conversejs .message.chat-msg .spoiler-toggle:before {
padding-right: 0.25em;
whitespace: nowrap; }
#conversejs .message.chat-msg .chat-msg-content {
#conversejs .message.chat-msg .chat-msg__content {
display: flex;
flex-direction: column;
justify-content: space-between;
align-items: stretch;
margin-left: 0.5rem;
width: 100%; }
#conversejs .message.chat-msg.headline .chat-msg-content {
#conversejs .message.chat-msg .chat-msg__content--action {
margin-left: 0; }
#conversejs .message.chat-msg .chat-msg-text {
#conversejs .message.chat-msg .chat-msg__body {
display: flex;
flex-direction: row;
justify-content: space-between;
width: 100%; }
#conversejs .message.chat-msg .chat-msg__message {
display: flex;
flex-direction: column;
width: 100%; }
#conversejs .message.chat-msg .chat-msg__edit-modal {
cursor: pointer;
padding-right: 0.5em; }
#conversejs .message.chat-msg.headline .chat-msg__body {
margin-left: 0; }
#conversejs .message.chat-msg .chat-msg__text {
padding: 0;
color: #555; }
#conversejs .message.chat-msg .chat-msg-text a {
color: #555;
width: 100%; }
#conversejs .message.chat-msg .chat-msg__text a {
word-wrap: break-word;
word-break: break-all; }
#conversejs .message.chat-msg .chat-msg-text .emojione {
#conversejs .message.chat-msg .chat-msg__text .emojione {
margin-bottom: -6px; }
#conversejs .message.chat-msg .chat-msg-media {
margin-top: 0.25rem; }
#conversejs .message.chat-msg .chat-msg-media a {
#conversejs .message.chat-msg .chat-msg__media {
margin-top: 0.25rem;
word-break: break-all; }
#conversejs .message.chat-msg .chat-msg__media a {
word-wrap: break-word; }
#conversejs .message.chat-msg .chat-msg-media audio {
#conversejs .message.chat-msg .chat-msg__media audio {
width: 100%; }
#conversejs .message.chat-msg .avatar {
#conversejs .message.chat-msg .chat-msg__actions .chat-msg__action {
height: 14px;
font-size: 14px;
padding: 0;
border: none;
opacity: 0;
background: transparent;
cursor: pointer; }
#conversejs .message.chat-msg .chat-msg__actions .chat-msg__action:focus {
display: block; }
#conversejs .message.chat-msg .chat-msg__avatar {
margin-top: 0.5em;
height: 36px;
vertical-align: middle;
width: 36px; }
#conversejs .message.chat-msg .chat-msg-heading {
#conversejs .message.chat-msg .chat-msg__heading {
width: 100%;
margin-top: 0.5em;
padding-right: 0.25rem;
padding-bottom: 0.25rem;
display: block; }
#conversejs .message.chat-msg .chat-msg-heading .chat-msg-author {
#conversejs .message.chat-msg .chat-msg__heading .chat-msg__author {
white-space: nowrap;
font-family: "Century Gothic", futura, "URW Gothic L", Verdana, sans-serif;
font-size: 115%; }
#conversejs .message.chat-msg .chat-msg-heading .chat-msg-author .badge {
#conversejs .message.chat-msg .chat-msg__heading .chat-msg__author .badge {
font-size: 80%;
font-family: "Helvetica", "Arial", sans-serif; }
#conversejs .message.chat-msg .chat-msg-heading .chat-msg-time {
#conversejs .message.chat-msg .chat-msg__heading .chat-msg__time {
padding-left: 0.25em;
color: #8c8c8c; }
#conversejs .message.chat-msg.chat-action {
display: block; }
#conversejs .message.chat-msg.chat-action .chat-msg-heading {
float: left;
#conversejs .message.chat-msg.chat-msg--action .chat-msg__content {
flex-wrap: wrap;
flex-direction: row;
justify-content: flex-start; }
#conversejs .message.chat-msg.chat-msg--action .chat-msg__text {
width: auto; }
#conversejs .message.chat-msg.chat-msg--action .chat-msg__heading {
margin-top: 0;
padding-bottom: 0; }
#conversejs .message.chat-msg.chat-msg-followup .chat-msg-heading,
#conversejs .message.chat-msg.chat-msg-followup .avatar {
padding-bottom: 0;
width: auto; }
#conversejs .message.chat-msg.chat-msg--action .chat-msg__author {
font-size: 14px; }
#conversejs .message.chat-msg.chat-msg--action .chat-msg__time {
margin-left: 0; }
#conversejs .message.chat-msg.chat-msg--followup .chat-msg__heading,
#conversejs .message.chat-msg.chat-msg--followup .chat-msg__avatar {
display: none; }
#conversejs .message.chat-msg.chat-msg-followup .chat-msg-content {
#conversejs .message.chat-msg.chat-msg--followup .chat-msg__content {
margin-left: 2.75rem; }
#conversejs .chatroom-body .message.onload {
animation: colorchange-chatmessage-muc 1s;
@ -8968,11 +8957,11 @@ body.reset {
#conversejs .chatroom-body .message .separator {
border: 0.5px solid #E77051; }
#conversejs.converse-overlayed .message.chat-msg.chat-msg-followup .chat-msg-content {
#conversejs.converse-overlayed .message.chat-msg.chat-msg--followup .chat-msg__body {
margin-left: 0; }
@media screen and (max-width: 767px) {
#conversejs:not(.converse-embedded) .message.chat-msg .chat-msg-author {
#conversejs:not(.converse-embedded) .message.chat-msg .chat-msg__author {
white-space: normal; } }
#conversejs.converse-overlayed #minimized-chats {
order: 100;
@ -9040,6 +9029,9 @@ body.reset {
#conversejs.converse-overlayed #minimized-chats .chat-head-message-count-hidden {
display: none; }
#conversejs.fullscreen #controlbox #chatrooms .bookmarks-list dl.rooms-list.bookmarks dd.available-chatroom a.open-room {
width: 80%; }
#conversejs [hidden] {
display: none; }
#conversejs .visually-hidden {

View File

@ -16,7 +16,7 @@
<noscript><p><img src="//stats.opkode.com/piwik.php?idsite=1" style="border:0;" alt="" /></p></noscript>
<![if gte IE 11]>
<link type="text/css" rel="stylesheet" media="screen" href="/css/converse.css" />
<script src="/dist/converse.min.js"></script>
<script src="/dist/converse.js"></script>
<![endif]>
<style>

File diff suppressed because it is too large Load Diff

1084
dist/converse.js vendored

File diff suppressed because it is too large Load Diff

View File

@ -110,7 +110,7 @@
<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,
You can connect to any publically accessible <a href="https://xmpp.org" target="_blank" rel="noopener">XMPP/Jabber</a> server,
either from a <a href="https://xmpp.net/directory.php">public provider</a>, or one you have set up yourself.
</p>
<h3>Don't have an XMPP/Jabber account?</h3>
@ -133,11 +133,11 @@
<li><a href="https://www.igniterealtime.org/projects/openfire/plugins.jsp" target="_blank" rel="noopener">Openfire</a></li>
<li><a href="https://modules.prosody.im/mod_conversejs.html" target="_blank" rel="noopener">Prosody</a></li>
<li><a href="https://github.com/mikemarsian/conversejs-rails" target="_blank" rel="noopener">Ruby on Rails</a></li>
<li><a href="http://github.com/collective/collective.converse" target="_blank" rel="noopener">Plone</a></li>
<li><a href="https://github.com/collective/collective.converse" target="_blank" rel="noopener">Plone</a></li>
<li><a href="https://pypi.python.org/pypi/django-conversejs" target="_blank" rel="noopener">Django (option 1)</a></li>
<li><a href="https://github.com/fpytloun/django-xmpp" target="_blank" rel="noopener">Django (option 2)</a></li>
<li><a href="https://github.com/devurandom/roundcube-converse.js-xmpp-plugin" target="_blank" rel="noopener">Roundcube</a></li>
<li><a href="http://wordpress.org/plugins/conversejs" target="_blank" rel="noopener">Wordpress</a></li>
<li><a href="https://wordpress.org/plugins/conversejs/" target="_blank" rel="noopener">Wordpress</a></li>
<li><a href="https://github.com/jcbrand/patterns.converse" target="_blank" rel="noopener">Patternslib</a></li>
<li><a href="https://github.com/keensoft/alfresco-js-chat-share" target="_blank" rel="noopener">Alfresco</a></li>
<li><a href="https://github.com/friendica/friendica-addons/tree/master/xmpp/converse" target="_blank" rel="noopener">Friendica</a></li>
@ -157,23 +157,24 @@
</li>
<li><a href="https://conversejs.org/docs/html/plugin_development.html">Plugin Architecture</a></li>
<li>Single-user and group chat</li>
<li>Multi-user chatrooms (<a href="http://xmpp.org/extensions/xep-0045.html" target="_blank" rel="noopener">XEP 45</a>)</li>
<li>Chatroom bookmarks (<a href="http://xmpp.org/extensions/xep-0048.html" target="_blank" rel="noopener">XEP 48</a>)</li>
<li>Direct invitations to chat rooms (<a href="http://xmpp.org/extensions/xep-0249.html" target="_blank" rel="noopener">XEP 249</a>)</li>
<li>vCard support (<a href="http://xmpp.org/extensions/xep-0054.html" target="_blank" rel="noopener">XEP 54</a>)</li>
<li>Service discovery (<a href="http://xmpp.org/extensions/xep-0030.html" target="_blank" rel="noopener">XEP 30</a>)</li>
<li>In-band registration (<a href="http://xmpp.org/extensions/xep-0077.html" target="_blank" rel="noopener">XEP 77</a>)</li>
<li>Roster item exchange (<a href="http://xmpp.org/extensions/xep-0144.html" target="_blank" rel="noopener">XEP 144</a>)</li>
<li>Multi-user chatrooms (<a href="https://xmpp.org/extensions/xep-0045.html" target="_blank" rel="noopener">XEP 45</a>)</li>
<li>Chatroom bookmarks (<a href="https://xmpp.org/extensions/xep-0048.html" target="_blank" rel="noopener">XEP 48</a>)</li>
<li>Direct invitations to chat rooms (<a href="https://xmpp.org/extensions/xep-0249.html" target="_blank" rel="noopener">XEP 249</a>)</li>
<li>vCard support (<a href="https://xmpp.org/extensions/xep-0054.html" target="_blank" rel="noopener">XEP 54</a>)</li>
<li>Service discovery (<a href="https://xmpp.org/extensions/xep-0030.html" target="_blank" rel="noopener">XEP 30</a>)</li>
<li>In-band registration (<a href="https://xmpp.org/extensions/xep-0077.html" target="_blank" rel="noopener">XEP 77</a>)</li>
<li>Roster item exchange (<a href="https://xmpp.org/extensions/xep-0144.html" target="_blank" rel="noopener">XEP 144</a>)</li>
<li>Custom status messages</li>
<li>Typing and chat state notifications (<a href="http://xmpp.org/extensions/xep-0085.html" target="_blank" rel="noopener">XEP 85</a>)</li>
<li>Typing and chat state notifications (<a href="https://xmpp.org/extensions/xep-0085.html" target="_blank" rel="noopener">XEP 85</a>)</li>
<li>Desktop notifications</li>
<li>File sharing (<a href="http://xmpp.org/extensions/xep-0363.html" target="_blank" rel="noopener">XEP 363</a>)</li>
<li>Messages appear in all connected chat clients (<a href="http://xmpp.org/extensions/xep-0280.html" target="_blank" rel="noopener">XEP 280</a>)</li>
<li>Third person "/me" messages (<a href="http://xmpp.org/extensions/xep-0245.html" target="_blank" rel="noopener">XEP 245</a>)</li>
<li>XMPP Ping (<a href="http://xmpp.org/extensions/xep-0199.html" target="_blank" rel="noopener">XEP 199</a>)</li>
<li>Server-side archiving of messages (<a href="http://xmpp.org/extensions/xep-0313.html" target="_blank" rel="noopener">XEP 313</a>)</li>
<li>Hidden messages (aka Spoilers) (<a href="http://xmpp.org/extensions/xep-0382.html" target="_blank" rel="noopener">XEP 382</a>)</li>
<li>Client state indication (<a href="http://xmpp.org/extensions/xep-0352.html" target="_blank" rel="noopener">XEP 352</a>)</li>
<li>File sharing (<a href="https://xmpp.org/extensions/xep-0363.html" target="_blank" rel="noopener">XEP 363</a>)</li>
<li>Messages appear in all connected chat clients (<a href="https://xmpp.org/extensions/xep-0280.html" target="_blank" rel="noopener">XEP 280</a>)</li>
<li>Third person "/me" messages (<a href="https://xmpp.org/extensions/xep-0245.html" target="_blank" rel="noopener">XEP 245</a>)</li>
<li>XMPP Ping (<a href="https://xmpp.org/extensions/xep-0199.html" target="_blank" rel="noopener">XEP 199</a>)</li>
<li>Server-side archiving of messages (<a href="https://xmpp.org/extensions/xep-0313.html" target="_blank" rel="noopener">XEP 313</a>)</li>
<li>Hidden messages (aka Spoilers) (<a href="https://xmpp.org/extensions/xep-0382.html" target="_blank" rel="noopener">XEP 382</a>)</li>
<li>Client state indication (<a href="https://xmpp.org/extensions/xep-0352.html" target="_blank" rel="noopener">XEP 352</a>)</li>
<li>Last Message Correction (<a href="https://xmpp.org/extensions/xep-0308.html" target="_blank" rel="noopener">XEP 308</a>)</li>
<li>Off-the-record encryption</li>
<li>Supports anonymous logins, see the <a href="https://conversejs.org/demo/anonymous.html" target="_blank" rel="noopener">anonymous login demo</a>.</li>
<li>Translated into 17 languages</li>
@ -189,11 +190,11 @@
<div class="col-lg-8 col-lg-offset-2">
<h2>Contact</h2>
<ul class="contact">
<li>Follow me on <a href="http://twitter.com/jcopkode" target="_blank" rel="noopener">Twitter</a>
<li>Follow me on <a href="https://twitter.com/jcopkode" target="_blank" rel="noopener">Twitter</a>
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 XMPP chatroom: <a href="xmpp:discuss@conference.conversejs.org" class="xmpp JSnocheck" title="Converse chat room">discuss@conference.conversejs.org</a>.</li>
<li>For technical support, you can ask on <a href="https://stackoverflow.com/questions/tagged/converse.js">Stack Overflow</a>
<li>The Converse XMPP chatroom: <a href="xmpp:discuss@conference.conversejs.org?join" 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>
@ -205,7 +206,7 @@
However, please don't contact me personally for free support, use
the other channels mentioned above.</br></br>
Here's my <a href="http://opkode.com/contact" target="_blank" rel="noopener">contact form</a>.</br>
Here's my <a href="https://opkode.com/contact.html" target="_blank" rel="noopener">contact form</a>.</br>
</p>
</div>
</div>
@ -229,7 +230,7 @@
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>.
<a href="https://opkode.com/contact.html" target="_blank" rel="noopener">contact us</a>.
</p>
</div>
</div>
@ -249,7 +250,7 @@
</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>.
own domain name, please <a href="https://opkode.com/contact.html" target="_blank" rel="noopener">contact us</a>.
</p>
<div class="privacy-policy">
@ -303,7 +304,7 @@
</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>.
transferal to another account, please <a href="https://opkode.com/contact.html" target="_blank" rel="noopener">contact us</a>.
</p>
<h4>Account deletion</h4>
<p>
@ -314,7 +315,7 @@
<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>
You can always <a href="https://opkode.com/contact.html" target="_blank" rel="noopener">contact us</a>
and we'll delete your account manually.
</p>
</div>
@ -350,7 +351,7 @@
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
For more information, please refer to <http://unlicense.org/>
For more information, please refer to <https://unlicense.org/>
@licend
*/
converse.initialize({

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@ -8,7 +8,7 @@ msgstr ""
"Project-Id-Version: Converse.js 3.3.4\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2018-05-17 11:19+0200\n"
"PO-Revision-Date: 2018-05-15 11:34+0000\n"
"PO-Revision-Date: 2018-07-02 15:32+0200\n"
"Last-Translator: ButterflyOfFire <ButterflyOfFire@protonmail.com>\n"
"Language-Team: Arabic <https://hosted.weblate.org/projects/conversejs/"
"translations/ar/>\n"
@ -18,7 +18,7 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 "
"&& n%100<=10 ? 3 : n%100>=11 ? 4 : 5;\n"
"X-Generator: Weblate 3.0-dev\n"
"X-Generator: Weblate 3.0\n"
#: dist/converse-no-dependencies.js:9853 dist/converse-no-dependencies.js:9882
msgid "Download"
@ -153,7 +153,7 @@ msgstr ""
#: dist/converse-no-dependencies.js:21257
msgid "Sorry, could not succesfully upload your file."
msgstr ""
msgstr "للأسف لم نتمكّن مِن القيام برفع ملفك بنجاح."
#: dist/converse-no-dependencies.js:21260
#, javascript-format
@ -162,7 +162,7 @@ msgstr "رد الخادم : \"%1$s\""
#: dist/converse-no-dependencies.js:21442
msgid "Sorry, looks like file upload is not supported by your server."
msgstr ""
msgstr "للأسف يبدو أن خاصية رفع الملفات لا يدعمها خادومكم."
#: dist/converse-no-dependencies.js:21452
#, javascript-format
@ -171,8 +171,7 @@ msgid ""
"which is %2$s."
msgstr ""
#: dist/converse-no-dependencies.js:22197
#, fuzzy
#: dist/converse-no-dependencies.js:18681
msgid "Show more"
msgstr "عرض المزيد"
@ -330,7 +329,11 @@ msgstr "كتب كأنه شخص ثالث"
msgid "Show this menu"
msgstr "إظهار هذه القائمة"
<<<<<<< HEAD
#: dist/converse-no-dependencies.js:19614
=======
#: dist/converse-no-dependencies.js:23164
>>>>>>> master
msgid "Are you sure you want to clear the messages from this conversation?"
msgstr "هل أنت متأكد أنك تود مسح الرسائل مِن نافذة المحادثة هذه ؟"
@ -344,11 +347,103 @@ msgstr "قد قطع الإتصال"
msgid "is busy"
msgstr "مشغول"
<<<<<<< HEAD
#: dist/converse-no-dependencies.js:19710
msgid "is online"
msgstr "متصل"
#: dist/converse-no-dependencies.js:22577
#, javascript-format
msgid "%1$s has invited you to join a chat room: %2$s"
msgstr "قام %1$s بدعوتك للإلتحاق بغرفة المحادثة : %2$s"
#: dist/converse-no-dependencies.js:22579
#, javascript-format
msgid ""
"%1$s has invited you to join a chat room: %2$s, and left the following "
"reason: \"%3$s\""
msgstr ""
#: dist/converse-no-dependencies.js:22956
#: dist/converse-no-dependencies.js:23041
#: dist/converse-no-dependencies.js:32286
msgid "Bookmark this room"
msgstr "إضافة هذه الغرفة إلى الفواصل المرجعية"
#: dist/converse-no-dependencies.js:23042
msgid "The name for this bookmark:"
msgstr "تسمية الفاصلة المرجعية :"
#: dist/converse-no-dependencies.js:23043
msgid "Would you like this room to be automatically joined upon startup?"
msgstr "هل تود الإلتحاق بهذه الغرفة آليا مباشَرةً بعد الإتصال ؟"
#: dist/converse-no-dependencies.js:23044
msgid "What should your nickname for this room be?"
msgstr "ما هو الإسم المُستعار الذي تريد استخدامه في غرفة المحادثة هذه ؟"
#: dist/converse-no-dependencies.js:23046
#: dist/converse-no-dependencies.js:25364
msgid "Save"
msgstr "حفظ"
#: dist/converse-no-dependencies.js:23047
#: dist/converse-no-dependencies.js:25360
#: dist/converse-no-dependencies.js:31362
msgid "Cancel"
msgstr "إلغاء"
#: dist/converse-no-dependencies.js:23120
#, javascript-format
msgid "Are you sure you want to remove the bookmark \"%1$s\"?"
msgstr "هل أنت متأكد أنك تريد إزالة الفاصلة المرجعية \"%1$s\" ؟"
#: dist/converse-no-dependencies.js:23236
msgid "Sorry, something went wrong while trying to save your bookmark."
msgstr "المعذرة، لقد طرأ هناك خطأ أثناء محاولة الإحتفاظ بالفواصل المرجعية."
#: dist/converse-no-dependencies.js:23315
#: dist/converse-no-dependencies.js:32284
msgid "Leave this room"
msgstr "الخروج مِن هذه الغرفة"
#: dist/converse-no-dependencies.js:23316
msgid "Remove this bookmark"
msgstr "إزالة هذه الفاصلة المرجعية"
#: dist/converse-no-dependencies.js:23317
#: dist/converse-no-dependencies.js:32285
msgid "Unbookmark this room"
msgstr "تنحية غرفة المحادثة مِن الفواصل المرجعية"
#: dist/converse-no-dependencies.js:23318
#: dist/converse-no-dependencies.js:28813
#: dist/converse-no-dependencies.js:32287
msgid "Show more information on this room"
msgstr "عرض المزيد مِن التفاصيل عن هذه الغرفة"
#: dist/converse-no-dependencies.js:23321
#: dist/converse-no-dependencies.js:28812
#: dist/converse-no-dependencies.js:32289
msgid "Click to open this room"
msgstr "أنقر لفتح غرفة المحادثة هذه"
#: dist/converse-no-dependencies.js:23357
msgid "Click to toggle the bookmarks list"
msgstr "أنقر للإنتقال إلى قائمة الإشارات المرجعية"
#: dist/converse-no-dependencies.js:23358
msgid "Bookmarks"
msgstr "الفواصل المرجعية"
#: dist/converse-no-dependencies.js:23546
=======
#: dist/converse-no-dependencies.js:23260
msgid "is online"
msgstr "متصل"
#: dist/converse-no-dependencies.js:23501
>>>>>>> master
msgid "XMPP Username:"
msgstr "إسم المستخدِم :"
@ -533,6 +628,13 @@ msgstr "إضافة مراسل"
#: dist/converse-no-dependencies.js:25288
msgid "Your Profile"
msgstr "ملفك الشخصي"
<<<<<<< HEAD
#: dist/converse-no-dependencies.js:25346
#: dist/converse-no-dependencies.js:25358
msgid "Close"
msgstr "إغلاق"
=======
#: dist/converse-no-dependencies.js:25293
#, fuzzy
@ -557,6 +659,7 @@ msgstr "المعذرة، لقد طرأ هناك خطأ أثناء محاولة
#: dist/converse-no-dependencies.js:25325
msgid "You can check your browser's developer console for any error output."
msgstr ""
>>>>>>> master
#: dist/converse-no-dependencies.js:25377
msgid "Custom status"
@ -657,7 +760,11 @@ msgstr "غرفة المحادثة هذه ليست مجهولة"
msgid "This room now shows unavailable members"
msgstr ""
<<<<<<< HEAD
#: dist/converse-no-dependencies.js:28646
=======
#: dist/converse-no-dependencies.js:28652
>>>>>>> master
#, fuzzy
msgid "This room does not show unavailable members"
msgstr "هذه القاعة لا تقوم بعرض الأعضاء المشغولين"
@ -827,7 +934,7 @@ msgstr "ليست تحت الإشراف"
#: dist/converse-no-dependencies.js:28777
msgid "Query for Chatrooms"
msgstr ""
msgstr "الإستعلام عن قاعات الدردشة"
#: dist/converse-no-dependencies.js:28778
msgid "Server address"
@ -869,22 +976,35 @@ msgstr "الإلتحاق بالغرفة"
msgid "Message"
msgstr "رسالة"
<<<<<<< HEAD
#: dist/converse-no-dependencies.js:29053
#, javascript-format
=======
#: dist/converse-no-dependencies.js:29058
#, fuzzy, javascript-format
>>>>>>> master
msgid "%1$s is no longer a moderator"
msgstr "لم يعُد %1$s مِن مُشْرِفي غرفة المحادثة."
msgstr "لم يعُد %1$s مِن مُشْرِفي غرفة المحادثة"
#: dist/converse-no-dependencies.js:29061
#, fuzzy, javascript-format
msgid "%1$s has been given a voice again"
msgstr "لقد تم طرد %1$s مِن غرفة المحادثة مؤقتًا"
<<<<<<< HEAD
#: dist/converse-no-dependencies.js:29059
=======
#: dist/converse-no-dependencies.js:29064
>>>>>>> master
#, javascript-format
msgid "%1$s has been muted"
msgstr "تم كتم %1$s"
<<<<<<< HEAD
#: dist/converse-no-dependencies.js:29062
=======
#: dist/converse-no-dependencies.js:29067
>>>>>>> master
#, javascript-format
msgid "%1$s is now a moderator"
msgstr "أصبح %1$s مُشرفًا"
@ -1081,9 +1201,14 @@ msgstr "غُرف المحادثة"
msgid "Add a new room"
msgstr "إضافة غرفة جديدة"
<<<<<<< HEAD
#: dist/converse-no-dependencies.js:29953
#, fuzzy
=======
#: dist/converse-no-dependencies.js:29983
>>>>>>> master
msgid "Query for rooms"
msgstr ""
msgstr "البحث عن قاعات"
#: dist/converse-no-dependencies.js:30022
#, javascript-format
@ -1410,6 +1535,8 @@ msgstr "تم التحقق منه"
#: dist/converse-no-dependencies.js:31192
msgid "finished"
msgstr "انتهى"
<<<<<<< HEAD
=======
#: dist/converse-no-dependencies.js:31788
#, javascript-format
@ -1419,6 +1546,7 @@ msgstr "المعذرة، لقد حدث هناك خطأ أثناء محاولة
#: dist/converse-no-dependencies.js:31936
msgid "This client does not allow presence subscriptions"
msgstr ""
>>>>>>> master
#: dist/converse-no-dependencies.js:32028
msgid "Click to hide these contacts"

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@ -7,7 +7,7 @@ msgstr ""
"Project-Id-Version: Converse.js 0.4\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2018-05-17 11:19+0200\n"
"PO-Revision-Date: 2018-05-07 18:37+0000\n"
"PO-Revision-Date: 2018-07-02 15:35+0200\n"
"Last-Translator: Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>\n"
"Language-Team: French <https://hosted.weblate.org/projects/conversejs/"
"translations/fr/>\n"
@ -92,50 +92,9 @@ msgstr "Marquer ce salon"
msgid "The name for this bookmark:"
msgstr "Nom de ce marque-page:"
#: dist/converse-no-dependencies.js:16466
msgid "Would you like this room to be automatically joined upon startup?"
msgstr "Voulez-vous rejoindre automatiquement ce salon au lancement?"
#: dist/converse-no-dependencies.js:16467
msgid "What should your nickname for this room be?"
msgstr "Quel alias devrait être utilisé pour ce salon?"
#: dist/converse-no-dependencies.js:16469
#: dist/converse-no-dependencies.js:25296
#: dist/converse-no-dependencies.js:25380
msgid "Save"
msgstr "Enregistrer"
#: dist/converse-no-dependencies.js:16470
#: dist/converse-no-dependencies.js:25376
#: dist/converse-no-dependencies.js:32190
msgid "Cancel"
msgstr "Annuler"
#: dist/converse-no-dependencies.js:16543
#, javascript-format
msgid "Are you sure you want to remove the bookmark \"%1$s\"?"
msgstr "Voulez-vous vraiment retirer le marque-page « %1$s»?"
#: dist/converse-no-dependencies.js:16659
msgid "Sorry, something went wrong while trying to save your bookmark."
msgstr ""
"Désolé, quelque chose sest mal passé pendant la sauvegarde de ce marque-"
"page."
#: dist/converse-no-dependencies.js:16738
#: dist/converse-no-dependencies.js:33112
msgid "Leave this room"
msgstr "Quitter ce salon"
#: dist/converse-no-dependencies.js:16739
msgid "Remove this bookmark"
msgstr "Retirer ce marque-page"
#: dist/converse-no-dependencies.js:16740
#: dist/converse-no-dependencies.js:33113
msgid "Unbookmark this room"
msgstr "Retirer ce salon"
#: dist/converse-no-dependencies.js:17811
msgid "Sorry, could not determine file upload URL."
msgstr "Désolé, impossible de déterminer lURL pour envoyer le fichier."
#: dist/converse-no-dependencies.js:16741
#: dist/converse-no-dependencies.js:28819
@ -187,32 +146,10 @@ msgstr ""
"La taille de votre fichier, %1$s, dépasse le maximum autorisé par votre "
"serveur, qui est %2$s."
#: dist/converse-no-dependencies.js:22197
#: dist/converse-no-dependencies.js:18681
msgid "Show more"
msgstr "Afficher plus"
#: dist/converse-no-dependencies.js:22248
msgid "Typing from another device"
msgstr "Saisie depuis un autre appareil"
#: dist/converse-no-dependencies.js:22250
msgid "is typing"
msgstr "écrit"
#: dist/converse-no-dependencies.js:22254
msgid "Stopped typing on the other device"
msgstr "Fin de saisie depuis lautre appareil"
#: dist/converse-no-dependencies.js:22256
msgid "has stopped typing"
msgstr "a arrêté décrire"
#: dist/converse-no-dependencies.js:22259
#: dist/converse-no-dependencies.js:23256
#: dist/converse-no-dependencies.js:30521
msgid "has gone away"
msgstr "est parti"
#: dist/converse-no-dependencies.js:22488
msgid "Close this chat box"
msgstr "Fermer cette fenêtre de discussion"
@ -325,7 +262,7 @@ msgstr "Cliquez pour écrire votre message en tant que spoiler"
msgid "Clear all messages"
msgstr "Supprimer tous les messages"
#: dist/converse-no-dependencies.js:22755
#: dist/converse-no-dependencies.js:19149
msgid "Insert emojis"
msgstr "Insérer un emoji"
@ -347,7 +284,7 @@ msgstr "Écrire à la troisième personne"
msgid "Show this menu"
msgstr "Afficher ce menu"
#: dist/converse-no-dependencies.js:23164
#: dist/converse-no-dependencies.js:19614
msgid "Are you sure you want to clear the messages from this conversation?"
msgstr "Voulez-vous vraiment effacer les messages de cette conversation?"
@ -361,7 +298,7 @@ msgstr "sest déconnecté"
msgid "is busy"
msgstr "est occupé"
#: dist/converse-no-dependencies.js:23260
#: dist/converse-no-dependencies.js:19710
msgid "is online"
msgstr "est en ligne"
@ -891,22 +828,22 @@ msgstr "Rejoindre"
msgid "Message"
msgstr "Message"
#: dist/converse-no-dependencies.js:29058
#: dist/converse-no-dependencies.js:29053
#, javascript-format
msgid "%1$s is no longer a moderator"
msgstr "%1$s nest plus un modérateur"
#: dist/converse-no-dependencies.js:29061
#: dist/converse-no-dependencies.js:29056
#, javascript-format
msgid "%1$s has been given a voice again"
msgstr "%1$s peut de nouveau parler"
#: dist/converse-no-dependencies.js:29064
#: dist/converse-no-dependencies.js:29059
#, javascript-format
msgid "%1$s has been muted"
msgstr "%1$s ne peut plus parler"
#: dist/converse-no-dependencies.js:29067
#: dist/converse-no-dependencies.js:29062
#, javascript-format
msgid "%1$s is now a moderator"
msgstr "%1$s est désormais un modérateur"

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@ -8,8 +8,8 @@ msgstr ""
"Project-Id-Version: Converse.js 0.4\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2018-05-17 11:19+0200\n"
"PO-Revision-Date: 2018-04-30 19:54+0000\n"
"Last-Translator: Nathan Follens <nathan@email.is>\n"
"PO-Revision-Date: 2018-07-03 10:22+0000\n"
"Last-Translator: Nathan Follens <nthn@unseen.is>\n"
"Language-Team: Dutch <https://hosted.weblate.org/projects/conversejs/"
"translations/nl/>\n"
"Language: nl\n"
@ -17,7 +17,7 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
"X-Generator: Weblate 3.0-dev\n"
"X-Generator: Weblate 3.1-dev\n"
"domain: converse\n"
"lang: nl\n"
"plural_forms: nplurals=2; plural=(n != 1);\n"
@ -177,39 +177,17 @@ msgstr ""
"De grootte van het bestand, %1$s, overschrijdt het maximum toegestaan door "
"je server, %2$s."
#: dist/converse-no-dependencies.js:22197
#: dist/converse-no-dependencies.js:18681
msgid "Show more"
msgstr "Meer tonen"
#: dist/converse-no-dependencies.js:22248
msgid "Typing from another device"
msgstr "Typt op een ander apparaat"
#: dist/converse-no-dependencies.js:22250
msgid "is typing"
msgstr "is aan typen"
#: dist/converse-no-dependencies.js:22254
msgid "Stopped typing on the other device"
msgstr "Gestopt met typen op het andere apparaat"
#: dist/converse-no-dependencies.js:22256
msgid "has stopped typing"
msgstr "is gestopt met typen"
#: dist/converse-no-dependencies.js:22259
#: dist/converse-no-dependencies.js:23256
#: dist/converse-no-dependencies.js:30521
msgid "has gone away"
msgstr "is afwezig"
#: dist/converse-no-dependencies.js:22488
msgid "Close this chat box"
msgstr "Dit gespreksvenster sluiten"
#: dist/converse-no-dependencies.js:22516
msgid "The User's Profile Image"
msgstr ""
msgstr "Profielafbeelding van gebruiker"
#: dist/converse-no-dependencies.js:22519
#: dist/converse-no-dependencies.js:25289
@ -220,18 +198,16 @@ msgstr "Sluiten"
#: dist/converse-no-dependencies.js:22520
#: dist/converse-no-dependencies.js:25290
msgid "Email"
msgstr ""
msgstr "E-mail"
#: dist/converse-no-dependencies.js:22521
#: dist/converse-no-dependencies.js:25291
#, fuzzy
msgid "Full Name"
msgstr "Naam"
msgstr "Volledige naam"
#: dist/converse-no-dependencies.js:22522
#, fuzzy
msgid "Jabber ID"
msgstr "XMPP-ID:"
msgstr "Jabber-ID"
#: dist/converse-no-dependencies.js:22523
#: dist/converse-no-dependencies.js:25292
@ -240,23 +216,22 @@ msgid "Nickname"
msgstr "Bijnaam"
#: dist/converse-no-dependencies.js:22524
#, fuzzy
msgid "Remove as contact"
msgstr "Voeg een contact toe"
msgstr "Contact verwijderen"
#: dist/converse-no-dependencies.js:22525
msgid "Refresh"
msgstr ""
msgstr "Verversen"
#: dist/converse-no-dependencies.js:22526
#: dist/converse-no-dependencies.js:25294
msgid "Role"
msgstr ""
msgstr "Rol"
#: dist/converse-no-dependencies.js:22527
#: dist/converse-no-dependencies.js:25297
msgid "URL"
msgstr ""
msgstr "URL"
#: dist/converse-no-dependencies.js:22566
#: dist/converse-no-dependencies.js:24293
@ -313,7 +288,7 @@ msgstr "Klik hier om een verborgen bericht te schrijven"
msgid "Clear all messages"
msgstr "Alle berichten wissen"
#: dist/converse-no-dependencies.js:22755
#: dist/converse-no-dependencies.js:19149
msgid "Insert emojis"
msgstr "Voeg smileys in"
@ -335,7 +310,7 @@ msgstr "Schrijf in de derde persoon"
msgid "Show this menu"
msgstr "Toon dit menu"
#: dist/converse-no-dependencies.js:23164
#: dist/converse-no-dependencies.js:19614
msgid "Are you sure you want to clear the messages from this conversation?"
msgstr "Weet je zeker dat je de berichten in dit gesprek wil wissen?"
@ -349,10 +324,96 @@ msgstr "is offline"
msgid "is busy"
msgstr "is bezet"
#: dist/converse-no-dependencies.js:23260
#: dist/converse-no-dependencies.js:19710
msgid "is online"
msgstr "is online"
#: dist/converse-no-dependencies.js:22577
#, javascript-format
msgid "%1$s has invited you to join a chat room: %2$s"
msgstr "%1$s heeft je uitgenodigd in het groepsgesprek: %2$s"
#: dist/converse-no-dependencies.js:22579
#, javascript-format
msgid ""
"%1$s has invited you to join a chat room: %2$s, and left the following "
"reason: \"%3$s\""
msgstr ""
"%1$s heeft je uitgenodigd in het groepsgesprek: %2$s. en gaf volgende reden: "
"%3$s"
#: dist/converse-no-dependencies.js:22956
#: dist/converse-no-dependencies.js:23041
#: dist/converse-no-dependencies.js:32286
msgid "Bookmark this room"
msgstr "Gesprek toevoegen aan bladwijzers"
#: dist/converse-no-dependencies.js:23042
msgid "The name for this bookmark:"
msgstr "De naam voor deze bladwijzer:"
#: dist/converse-no-dependencies.js:23043
msgid "Would you like this room to be automatically joined upon startup?"
msgstr "Wil je automatisch deelnemen aan dit groepsgesprek bij opstarten?"
#: dist/converse-no-dependencies.js:23044
msgid "What should your nickname for this room be?"
msgstr "Wat moet je bijnaam in dit groepsgesprek zijn?"
#: dist/converse-no-dependencies.js:23046
#: dist/converse-no-dependencies.js:25364
msgid "Save"
msgstr "Opslaan"
#: dist/converse-no-dependencies.js:23047
#: dist/converse-no-dependencies.js:25360
#: dist/converse-no-dependencies.js:31362
msgid "Cancel"
msgstr "Annuleren"
#: dist/converse-no-dependencies.js:23120
#, javascript-format
msgid "Are you sure you want to remove the bookmark \"%1$s\"?"
msgstr "Weet je zeker dat je de bladwijzer %1$s wil verwijderen?"
#: dist/converse-no-dependencies.js:23236
msgid "Sorry, something went wrong while trying to save your bookmark."
msgstr "Sorry, er ging iets mis bij het opslaan van je bladwijzer."
#: dist/converse-no-dependencies.js:23315
#: dist/converse-no-dependencies.js:32284
msgid "Leave this room"
msgstr "Dit gesprek verlaten"
#: dist/converse-no-dependencies.js:23316
msgid "Remove this bookmark"
msgstr "Deze bladwijzer verwijderen"
#: dist/converse-no-dependencies.js:23317
#: dist/converse-no-dependencies.js:32285
msgid "Unbookmark this room"
msgstr "Bladwijzer voor dit gesprek verwijderen"
#: dist/converse-no-dependencies.js:23318
#: dist/converse-no-dependencies.js:28813
#: dist/converse-no-dependencies.js:32287
msgid "Show more information on this room"
msgstr "Toon meer informatie over dit groepsgesprek"
#: dist/converse-no-dependencies.js:23321
#: dist/converse-no-dependencies.js:28812
#: dist/converse-no-dependencies.js:32289
msgid "Click to open this room"
msgstr "Klik om dit groepsgesprek te openen"
#: dist/converse-no-dependencies.js:23357
msgid "Click to toggle the bookmarks list"
msgstr "Klik om de lijst met bladwijzers te tonen/verbergen"
#: dist/converse-no-dependencies.js:23358
msgid "Bookmarks"
msgstr "Bladwijzers"
#: dist/converse-no-dependencies.js:23501
msgid "XMPP Username:"
msgstr "XMPP-gebruikersnaam:"
@ -540,28 +601,29 @@ msgid "Your Profile"
msgstr "Je profiel"
#: dist/converse-no-dependencies.js:25293
#, fuzzy
msgid "XMPP Address (JID)"
msgstr "XMPP-adres"
msgstr "XMPP-adres (JID)"
#: dist/converse-no-dependencies.js:25295
msgid ""
"Use commas to separate multiple roles. Your roles are shown next to your "
"name on your chat messages."
msgstr ""
"Gebruik kommas om meerdere rollen op te geven. Je rollen worden naast je "
"naam op je chatberichten getoond."
#: dist/converse-no-dependencies.js:25298
msgid "Your avatar image"
msgstr ""
msgstr "Jouw avatarafbeelding"
#: dist/converse-no-dependencies.js:25325
#, fuzzy
msgid "Sorry, an error happened while trying to save your profile data."
msgstr "Sorry, er ging iets mis bij het opslaan van je bladwijzer."
msgstr "Sorry, er ging iets mis bij het opslaan van je profielgegevens."
#: dist/converse-no-dependencies.js:25325
msgid "You can check your browser's developer console for any error output."
msgstr ""
"Je kan de ontwikkelaarsconsole van je browser nakijken voor foutenuitvoer."
#: dist/converse-no-dependencies.js:25377
msgid "Custom status"
@ -878,22 +940,22 @@ msgstr "Deelnemen"
msgid "Message"
msgstr "Bericht"
#: dist/converse-no-dependencies.js:29058
#: dist/converse-no-dependencies.js:29053
#, javascript-format
msgid "%1$s is no longer a moderator"
msgstr "%1$s is geen moderator meer"
#: dist/converse-no-dependencies.js:29061
#: dist/converse-no-dependencies.js:29056
#, javascript-format
msgid "%1$s has been given a voice again"
msgstr "%1$s heeft terug een stem"
#: dist/converse-no-dependencies.js:29064
#: dist/converse-no-dependencies.js:29059
#, javascript-format
msgid "%1$s has been muted"
msgstr "%1$s is gedempt"
#: dist/converse-no-dependencies.js:29067
#: dist/converse-no-dependencies.js:29062
#, javascript-format
msgid "%1$s is now a moderator"
msgstr "%1$s is nu een moderator"
@ -924,6 +986,8 @@ msgid ""
"Sorry, an error happened while running the command. Check your browser's "
"developer console for details."
msgstr ""
"Sorry, er trad een fout op bij het uitvoeren van de opdracht. Controleer de "
"ontwikkelaarsconsole van je browser voor details."
#: dist/converse-no-dependencies.js:29263
msgid "Change user's affiliation to admin"
@ -1114,26 +1178,24 @@ msgid "This user can NOT send messages in this room."
msgstr "Deze gebruiker kan GEEN berichten sturen in dit groepsgesprek."
#: dist/converse-no-dependencies.js:30026
#, fuzzy
msgid "Moderator"
msgstr "Gemodereerd"
msgstr "Moderator"
#: dist/converse-no-dependencies.js:30027
msgid "Visitor"
msgstr ""
msgstr "Bezoeker"
#: dist/converse-no-dependencies.js:30028
msgid "Owner"
msgstr ""
msgstr "Eigenaar"
#: dist/converse-no-dependencies.js:30029
#, fuzzy
msgid "Member"
msgstr "Alleen-leden"
msgstr "Lid"
#: dist/converse-no-dependencies.js:30030
msgid "Admin"
msgstr ""
msgstr "Beheerder"
#: dist/converse-no-dependencies.js:30082
msgid "Occupants"

File diff suppressed because one or more lines are too long

View File

@ -8,8 +8,8 @@ msgstr ""
"Project-Id-Version: Converse.js 3.3.4\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2018-05-17 11:19+0200\n"
"PO-Revision-Date: 2018-04-30 19:58+0000\n"
"Last-Translator: Nathan Follens <nathan@email.is>\n"
"PO-Revision-Date: 2018-07-03 10:26+0000\n"
"Last-Translator: Nathan Follens <nthn@unseen.is>\n"
"Language-Team: Flemish <https://hosted.weblate.org/projects/conversejs/"
"translations/nl_BE/>\n"
"Language: nl_BE\n"
@ -17,7 +17,7 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
"X-Generator: Weblate 3.0-dev\n"
"X-Generator: Weblate 3.1-dev\n"
#: dist/converse-no-dependencies.js:9853 dist/converse-no-dependencies.js:9882
msgid "Download"
@ -174,7 +174,7 @@ msgstr ""
"De grootte van het bestand, %1$s, overschrijd het maximum toegelaten door "
"uwe server, %2$s."
#: dist/converse-no-dependencies.js:22197
#: dist/converse-no-dependencies.js:18681
msgid "Show more"
msgstr "Meer tonen"
@ -206,7 +206,7 @@ msgstr "Dit gespreksvenster sluiten"
#: dist/converse-no-dependencies.js:22516
msgid "The User's Profile Image"
msgstr ""
msgstr "Profielafbeelding van gebruiker"
#: dist/converse-no-dependencies.js:22519
#: dist/converse-no-dependencies.js:25289
@ -217,16 +217,16 @@ msgstr "Sluiten"
#: dist/converse-no-dependencies.js:22520
#: dist/converse-no-dependencies.js:25290
msgid "Email"
msgstr ""
msgstr "E-mail"
#: dist/converse-no-dependencies.js:22521
#: dist/converse-no-dependencies.js:25291
msgid "Full Name"
msgstr ""
msgstr "Volledige naam"
#: dist/converse-no-dependencies.js:22522
msgid "Jabber ID"
msgstr ""
msgstr "Jabber-ID"
#: dist/converse-no-dependencies.js:22523
#: dist/converse-no-dependencies.js:25292
@ -235,23 +235,22 @@ msgid "Nickname"
msgstr "Bijnaam"
#: dist/converse-no-dependencies.js:22524
#, fuzzy
msgid "Remove as contact"
msgstr "Voeg een contact toe"
msgstr "Contact verwijderen"
#: dist/converse-no-dependencies.js:22525
msgid "Refresh"
msgstr ""
msgstr "Vernieuwen"
#: dist/converse-no-dependencies.js:22526
#: dist/converse-no-dependencies.js:25294
msgid "Role"
msgstr ""
msgstr "Rol"
#: dist/converse-no-dependencies.js:22527
#: dist/converse-no-dependencies.js:25297
msgid "URL"
msgstr ""
msgstr "URL"
#: dist/converse-no-dependencies.js:22566
#: dist/converse-no-dependencies.js:24293
@ -261,7 +260,7 @@ msgstr "Zij ge zeker da ge dit contact wild verwijderen?"
#: dist/converse-no-dependencies.js:22575
#: dist/converse-no-dependencies.js:25325
msgid "Error"
msgstr ""
msgstr "Fout"
#: dist/converse-no-dependencies.js:22575
#: dist/converse-no-dependencies.js:24301
@ -308,7 +307,7 @@ msgstr "Klikt hier voor een verborgen bericht te schrijven"
msgid "Clear all messages"
msgstr "Alle berichten wissen"
#: dist/converse-no-dependencies.js:22755
#: dist/converse-no-dependencies.js:19149
msgid "Insert emojis"
msgstr "Voegd smileys in"
@ -330,7 +329,7 @@ msgstr "Schrijfd in den derde persoon"
msgid "Show this menu"
msgstr "Toond dit menu"
#: dist/converse-no-dependencies.js:23164
#: dist/converse-no-dependencies.js:19614
msgid "Are you sure you want to clear the messages from this conversation?"
msgstr "Zij ge zeker da ge de berichten in dit gesprek wild wissen?"
@ -344,10 +343,96 @@ msgstr "is offline"
msgid "is busy"
msgstr "is bezet"
#: dist/converse-no-dependencies.js:23260
#: dist/converse-no-dependencies.js:19710
msgid "is online"
msgstr "is online"
#: dist/converse-no-dependencies.js:22577
#, javascript-format
msgid "%1$s has invited you to join a chat room: %2$s"
msgstr "%1$s heefd u uitgenodigd in het groepsgesprek: %2$s"
#: dist/converse-no-dependencies.js:22579
#, javascript-format
msgid ""
"%1$s has invited you to join a chat room: %2$s, and left the following "
"reason: \"%3$s\""
msgstr ""
"%1$s heefd u uitgenodigd in het groepsgesprek: %2$s. en gaf volgende reden: "
"%3$s"
#: dist/converse-no-dependencies.js:22956
#: dist/converse-no-dependencies.js:23041
#: dist/converse-no-dependencies.js:32286
msgid "Bookmark this room"
msgstr "Gesprek toevoegen aan bladwijzers"
#: dist/converse-no-dependencies.js:23042
msgid "The name for this bookmark:"
msgstr "De naam voor dezen bladwijzer:"
#: dist/converse-no-dependencies.js:23043
msgid "Would you like this room to be automatically joined upon startup?"
msgstr "Wild gautomatisch deelnemen aan dit groepsgesprek bij opstarten?"
#: dist/converse-no-dependencies.js:23044
msgid "What should your nickname for this room be?"
msgstr "Wa moet uwen bijnaam in dit groepsgesprek zijn?"
#: dist/converse-no-dependencies.js:23046
#: dist/converse-no-dependencies.js:25364
msgid "Save"
msgstr "Opslaan"
#: dist/converse-no-dependencies.js:23047
#: dist/converse-no-dependencies.js:25360
#: dist/converse-no-dependencies.js:31362
msgid "Cancel"
msgstr "Annuleren"
#: dist/converse-no-dependencies.js:23120
#, javascript-format
msgid "Are you sure you want to remove the bookmark \"%1$s\"?"
msgstr "Zij ge zeker da ge den bladwijzer %1$s wild verwijderen?"
#: dist/converse-no-dependencies.js:23236
msgid "Sorry, something went wrong while trying to save your bookmark."
msgstr "Sorry, der ging iets mis bij het opslaan van uwen bladwijzer."
#: dist/converse-no-dependencies.js:23315
#: dist/converse-no-dependencies.js:32284
msgid "Leave this room"
msgstr "Dit gesprek verlaten"
#: dist/converse-no-dependencies.js:23316
msgid "Remove this bookmark"
msgstr "Dezen bladwijzer verwijderen"
#: dist/converse-no-dependencies.js:23317
#: dist/converse-no-dependencies.js:32285
msgid "Unbookmark this room"
msgstr "Bladwijzer voor dit gesprek verwijderen"
#: dist/converse-no-dependencies.js:23318
#: dist/converse-no-dependencies.js:28813
#: dist/converse-no-dependencies.js:32287
msgid "Show more information on this room"
msgstr "Toond meer informatie over dit groepsgesprek"
#: dist/converse-no-dependencies.js:23321
#: dist/converse-no-dependencies.js:28812
#: dist/converse-no-dependencies.js:32289
msgid "Click to open this room"
msgstr "Klikt voor dit groepsgesprek topenen"
#: dist/converse-no-dependencies.js:23357
msgid "Click to toggle the bookmarks list"
msgstr "Klikt voor de lijst met bladwijzers te tonen/verbergen"
#: dist/converse-no-dependencies.js:23358
msgid "Bookmarks"
msgstr "Bladwijzers"
#: dist/converse-no-dependencies.js:23501
msgid "XMPP Username:"
msgstr "XMPP-gebruikersnaam:"
@ -535,28 +620,30 @@ msgid "Your Profile"
msgstr "Uw profiel"
#: dist/converse-no-dependencies.js:25293
#, fuzzy
msgid "XMPP Address (JID)"
msgstr "XMPP-adres"
msgstr "XMPP-adres (JID)"
#: dist/converse-no-dependencies.js:25295
msgid ""
"Use commas to separate multiple roles. Your roles are shown next to your "
"name on your chat messages."
msgstr ""
"Gebruikt kommas voor meerdere rollen op te geven. Uw rollen worden naast "
"uwe naam op uw berichten weergegeven."
#: dist/converse-no-dependencies.js:25298
msgid "Your avatar image"
msgstr ""
msgstr "Uw avatarafbeelding"
#: dist/converse-no-dependencies.js:25325
#, fuzzy
msgid "Sorry, an error happened while trying to save your profile data."
msgstr "Sorry, der ging iets mis bij het opslaan van uwen bladwijzer."
msgstr ""
"Sorry, der is een fout opgetreden bij het opslaan van uw profielgegevens."
#: dist/converse-no-dependencies.js:25325
msgid "You can check your browser's developer console for any error output."
msgstr ""
"Ge kunt de ontwikkelaarsconsole van uwen browser nakijken voor foutenuitvoer."
#: dist/converse-no-dependencies.js:25377
msgid "Custom status"
@ -873,22 +960,22 @@ msgstr "Deelnemen"
msgid "Message"
msgstr "Bericht"
#: dist/converse-no-dependencies.js:29058
#: dist/converse-no-dependencies.js:29053
#, javascript-format
msgid "%1$s is no longer a moderator"
msgstr "%1$s is gene moderator meer"
#: dist/converse-no-dependencies.js:29061
#: dist/converse-no-dependencies.js:29056
#, javascript-format
msgid "%1$s has been given a voice again"
msgstr "%1$s heefd terug een stem"
#: dist/converse-no-dependencies.js:29064
#: dist/converse-no-dependencies.js:29059
#, javascript-format
msgid "%1$s has been muted"
msgstr "%1$s is gedempt"
#: dist/converse-no-dependencies.js:29067
#: dist/converse-no-dependencies.js:29062
#, javascript-format
msgid "%1$s is now a moderator"
msgstr "%1$s is nu ne moderator"
@ -919,6 +1006,8 @@ msgid ""
"Sorry, an error happened while running the command. Check your browser's "
"developer console for details."
msgstr ""
"Sorry, der is een fout opgetreden bij het uitvoeren van de opdracht. "
"Controleert den ontwikkelaarsconsole van uwen browser voor details."
#: dist/converse-no-dependencies.js:29263
msgid "Change user's affiliation to admin"
@ -1109,26 +1198,24 @@ msgid "This user can NOT send messages in this room."
msgstr "Deze gebruiker kan GEEN berichten sturen in dit groepsgesprek."
#: dist/converse-no-dependencies.js:30026
#, fuzzy
msgid "Moderator"
msgstr "Gemodereerd"
msgstr "Moderator"
#: dist/converse-no-dependencies.js:30027
msgid "Visitor"
msgstr ""
msgstr "Bezoeker"
#: dist/converse-no-dependencies.js:30028
msgid "Owner"
msgstr ""
msgstr "Eigenaar"
#: dist/converse-no-dependencies.js:30029
#, fuzzy
msgid "Member"
msgstr "Alleen-leden"
msgstr "Lid"
#: dist/converse-no-dependencies.js:30030
msgid "Admin"
msgstr ""
msgstr "Beheerder"
#: dist/converse-no-dependencies.js:30082
msgid "Occupants"

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

View File

@ -1,643 +0,0 @@
(function (root, factory) {
var translations = {
"domain": "converse",
"locale_data": {
"converse": {
"": {
"Project-Id-Version": "Converse.js 0.6.3",
"Report-Msgid-Bugs-To": "",
"POT-Creation-Date": "2013-09-15 21:55+0200",
"PO-Revision-Date": "2014-07-07 11:02+0200",
"Last-Translator": "Alan Meira <alan@engarte.com>",
"Language-Team": "Brazilian Portuguese",
"Language": "pt_BR",
"MIME-Version": "1.0",
"Content-Type": "text/plain; charset=UTF-8",
"Content-Transfer-Encoding": "8bit",
"Plural-Forms": "nplurals=2; plural=(n > 1);",
"domain": "converse",
"lang": "pt_BR",
"plural_forms": "nplurals=2; plural=(n != 1);"
},
"unencrypted": [
null,
"não-criptografado"
],
"unverified": [
null,
"não-verificado"
],
"verified": [
null,
"não-verificado"
],
"finished": [
null,
"finalizado"
],
"Disconnected": [
null,
"Desconectado"
],
"Error": [
null,
"Erro"
],
"Connecting": [
null,
"Conectando"
],
"Connection Failed": [
null,
"Falha de conexão"
],
"Authenticating": [
null,
"Autenticando"
],
"Authentication Failed": [
null,
"Falha de autenticação"
],
"Disconnecting": [
null,
"Desconectando"
],
"Re-establishing encrypted session": [
null,
"Reestabelecendo sessão criptografada"
],
"Your browser needs to generate a private key, which will be used in your encrypted chat session. This can take up to 30 seconds during which your browser might freeze and become unresponsive.": [
null,
"Seu navegador precisa gerar uma chave-privada, que será usada em sua sessão criptografada de bate-papo. Isso pode levar até 30 segundos durante os quais seu navegador poderá se travar ou não responder."
],
"Private key generated.": [
null,
"Chave-privada gerada:"
],
"Authentication request from %1$s\n\nYour buddy is attempting to verify your identity, by asking you the question below.\n\n%2$s": [
null,
"Pedido de autenticação de %$s\n\nSeu contato está tentando verificar sua identidade, perguntando a questão abaixo.\n\n%2$s"
],
"Could not verify this user's identify.": [
null,
"Não foi possível verificar a identidade deste usuário."
],
"Personal message": [
null,
"Mensagem pessoal"
],
"Start encrypted conversation": [
null,
"Iniciar conversa criptografada"
],
"Refresh encrypted conversation": [
null,
"Atualizar conversa criptografada"
],
"End encrypted conversation": [
null,
"Finalizar conversa criptografada"
],
"Verify with SMP": [
null,
"Verificar com SMP"
],
"Verify with fingerprints": [
null,
"Verificar com assinatura digital"
],
"What's this?": [
null,
"O que é isso?"
],
"me": [
null,
"eu"
],
"Show this menu": [
null,
"Mostrar o menu"
],
"Write in the third person": [
null,
"Escrever em terceira pessoa"
],
"Remove messages": [
null,
"Remover mensagens"
],
"Your message could not be sent": [
null,
"Sua mensagem não pôde ser enviada"
],
"We received an unencrypted message": [
null,
"Recebemos uma mensagem não-criptografada"
],
"We received an unreadable encrypted message": [
null,
"Recebemos uma mensagem não-criptografada ilegível"
],
"This user has requested an encrypted session.": [
null,
"Usuário pediu uma sessão criptografada."
],
"Here are the fingerprints, please confirm them with %1$s, outside of this chat.\n\nFingerprint for you, %2$s: %3$s\n\nFingerprint for %1$s: %4$s\n\nIf you have confirmed that the fingerprints match, click OK, otherwise click Cancel.": [
null,
"Aqui estão as assinaturas digitais, por favor confirme elas com %1$s, fora deste chat.\n\nAssinaturas para você, %2$s: %3$s\n\nAssinaturas para %1$s: %4$s\n\nSe você tiver confirmado que as assinaturas conferem, clique OK, caso contrário, clique Cancel."
],
"You will be prompted to provide a security question and then an answer to that question.\n\nYour buddy will then be prompted the same question and if they type the exact same answer (case sensitive), their identity will have been verified.": [
null,
"Será solicitado que você informe uma pergunta de segurança e também uma resposta.\n\nNós iremos, então, transfeir a pergunta para seu contato e caso ele envie corretamente a mesma resposta (caso sensitivo), a identidade dele será verificada."
],
"What is your security question?": [
null,
"Qual é a sua pergunta de segurança?"
],
"What is the answer to the security question?": [
null,
"Qual é a resposta para a pergunta de segurança?"
],
"Invalid authentication scheme provided": [
null,
"Schema de autenticação fornecido é inválido"
],
"Your messages are not encrypted anymore": [
null,
"Suas mensagens não estão mais criptografadas"
],
"Your messages are now encrypted but your buddy's identity has not been verified.": [
null,
"Suas mensagens estão agora criptografadas mas a identidade do contato não foi confirmada."
],
"Your buddy's identify has been verified.": [
null,
"A identidade do contato foi verificada."
],
"Your buddy has ended encryption on their end, you should do the same.": [
null,
"Seu contato parou de usar criptografia, você deveria fazer o mesmo."
],
"Your messages are not encrypted. Click here to enable OTR encryption.": [
null,
"Suas mensagens não estão criptografadas. Clique aqui para habilitar criptografia OTR."
],
"Your messages are encrypted, but your buddy has not been verified.": [
null,
"Suas mensagens estão criptografadas, mas seu contato não foi verificado."
],
"Your messages are encrypted and your buddy verified.": [
null,
"Suas mensagens estão criptografadas e seu contato verificado."
],
"Your buddy has closed their end of the private session, you should do the same": [
null,
"Seu contato fechou a sessão privada, você deveria fazer o mesmo"
],
"Contacts": [
null,
"Contatos"
],
"Online": [
null,
"Online"
],
"Busy": [
null,
"Ocupado"
],
"Away": [
null,
"Ausente"
],
"Offline": [
null,
"Offline"
],
"Click to add new chat contacts": [
null,
"Clique para adicionar novos contatos ao chat"
],
"Add a contact": [
null,
"Adicionar contato"
],
"Contact username": [
null,
"Usuário do contatt"
],
"Add": [
null,
"Adicionar"
],
"Contact name": [
null,
"Nome do contato"
],
"Search": [
null,
"Procurar"
],
"No users found": [
null,
"Não foram encontrados usuários"
],
"Click to add as a chat contact": [
null,
"Clique para adicionar como um contato do chat"
],
"Click to open this room": [
null,
"CLique para abrir a sala"
],
"Show more information on this room": [
null,
"Mostrar mais informações nessa sala"
],
"Description:": [
null,
"Descrição:"
],
"Occupants:": [
null,
"Ocupantes:"
],
"Features:": [
null,
"Recursos:"
],
"Requires authentication": [
null,
"Requer autenticação"
],
"Hidden": [
null,
"Escondido"
],
"Requires an invitation": [
null,
"Requer um convite"
],
"Moderated": [
null,
"Moderado"
],
"Non-anonymous": [
null,
"Não anônimo"
],
"Open room": [
null,
"Sala aberta"
],
"Permanent room": [
null,
"Sala permanente"
],
"Public": [
null,
"Público"
],
"Semi-anonymous": [
null,
"Semi anônimo"
],
"Temporary room": [
null,
"Sala temporária"
],
"Unmoderated": [
null,
"Sem moderação"
],
"Rooms": [
null,
"Salas"
],
"Room name": [
null,
"Nome da sala"
],
"Nickname": [
null,
"Apelido"
],
"Server": [
null,
"Server"
],
"Join": [
null,
"Entrar"
],
"Show rooms": [
null,
"Mostrar salas"
],
"No rooms on %1$s": [
null,
"Sem salas em %1$s"
],
"Rooms on %1$s": [
null,
"Salas em %1$s"
],
"Set chatroom topic": [
null,
"Definir tópico do chat"
],
"Kick user from chatroom": [
null,
"Expulsar usuário do chat"
],
"Ban user from chatroom": [
null,
"Banir usuário do chat"
],
"Message": [
null,
"Mensagem"
],
"Save": [
null,
"Salvar"
],
"Cancel": [
null,
"Cancelar"
],
"An error occurred while trying to save the form.": [
null,
"Ocorreu um erro enquanto tentava salvar o formulário"
],
"This chatroom requires a password": [
null,
"Esse chat precisa de senha"
],
"Password: ": [
null,
"Senha: "
],
"Submit": [
null,
"Enviar"
],
"This room is not anonymous": [
null,
"Essa sala não é anônima"
],
"This room now shows unavailable members": [
null,
"Agora esta sala mostra membros indisponíveis"
],
"This room does not show unavailable members": [
null,
"Essa sala não mostra membros indisponíveis"
],
"Non-privacy-related room configuration has changed": [
null,
"Configuraçõs não relacionadas à privacidade mudaram"
],
"Room logging is now enabled": [
null,
"O log da sala está ativado"
],
"Room logging is now disabled": [
null,
"O log da sala está desativado"
],
"This room is now non-anonymous": [
null,
"Esse sala é não anônima"
],
"This room is now semi-anonymous": [
null,
"Essa sala agora é semi anônima"
],
"This room is now fully-anonymous": [
null,
"Essa sala agora é totalmente anônima"
],
"A new room has been created": [
null,
"Uma nova sala foi criada"
],
"Your nickname has been changed": [
null,
"Seu apelido foi mudado"
],
"<strong>%1$s</strong> has been banned": [
null,
"<strong>%1$s</strong> foi banido"
],
"<strong>%1$s</strong> has been kicked out": [
null,
"<strong>%1$s</strong> foi expulso"
],
"<strong>%1$s</strong> has been removed because of an affiliation change": [
null,
"<srtong>%1$s</strong> foi removido por causa de troca de associação"
],
"<strong>%1$s</strong> has been removed for not being a member": [
null,
"<strong>%1$s</strong> foi removido por não ser um membro"
],
"You have been banned from this room": [
null,
"Você foi banido dessa sala"
],
"You have been kicked from this room": [
null,
"Você foi expulso dessa sala"
],
"You have been removed from this room because of an affiliation change": [
null,
"Você foi removido da sala devido a uma mudança de associação"
],
"You have been removed from this room because the room has changed to members-only and you're not a member": [
null,
"Você foi removido da sala porque ela foi mudada para somente membrose você não é um membro"
],
"You have been removed from this room because the MUC (Multi-user chat) service is being shut down.": [
null,
"Você foi removido da sala devido a MUC (Multi-user chat)o serviço está sendo desligado"
],
"You are not on the member list of this room": [
null,
"Você não é membro dessa sala"
],
"No nickname was specified": [
null,
"Você não escolheu um apelido "
],
"You are not allowed to create new rooms": [
null,
"Você não tem permitição de criar novas salas"
],
"Your nickname doesn't conform to this room's policies": [
null,
"Seu apelido não está de acordo com as regras da sala"
],
"Your nickname is already taken": [
null,
"Seu apelido já foi escolhido"
],
"This room does not (yet) exist": [
null,
"A sala não existe (ainda)"
],
"This room has reached it's maximum number of occupants": [
null,
"A sala atingiu o número máximo de ocupantes"
],
"Topic set by %1$s to: %2$s": [
null,
"Topico definido por %1$s para: %2$s"
],
"This user is a moderator": [
null,
"Esse usuário é o moderador"
],
"This user can send messages in this room": [
null,
"Esse usuário pode enviar mensagens nessa sala"
],
"This user can NOT send messages in this room": [
null,
"Esse usuário NÃO pode enviar mensagens nessa sala"
],
"Click to chat with this contact": [
null,
"Clique para conversar com o contato"
],
"Click to remove this contact": [
null,
"Clique para remover o contato"
],
"This contact is busy": [
null,
"Este contato está ocupado"
],
"This contact is online": [
null,
"Este contato está online"
],
"This contact is offline": [
null,
"Este contato está offline"
],
"This contact is unavailable": [
null,
"Este contato está indisponível"
],
"This contact is away for an extended period": [
null,
"Este contato está ausente por um longo período"
],
"This contact is away": [
null,
"Este contato está ausente"
],
"Contact requests": [
null,
"Solicitação de contatos"
],
"My contacts": [
null,
"Meus contatos"
],
"Pending contacts": [
null,
"Contados pendentes"
],
"Custom status": [
null,
"Status customizado"
],
"Click to change your chat status": [
null,
"Clique para mudar seu status no chat"
],
"Click here to write a custom status message": [
null,
"Clique aqui para customizar a mensagem de status"
],
"online": [
null,
"online"
],
"busy": [
null,
"ocupado"
],
"away for long": [
null,
"ausente a bastante tempo"
],
"away": [
null,
"ausente"
],
"I am %1$s": [
null,
"Estou %1$s"
],
"Sign in": [
null,
"Conectar-se"
],
"XMPP/Jabber Username:": [
null,
"Usuário XMPP/Jabber:"
],
"Password:": [
null,
"Senha:"
],
"Log In": [
null,
"Entrar"
],
"BOSH Service URL:": [
null,
"URL de serviço BOSH:"
],
"Online Contacts": [
null,
"Contatos online"
],
"%1$s is typing": [
null,
"%1$s está digitando"
],
"Connected": [
null,
"Conectado"
],
"Attached": [
null,
"Anexado"
]
,
"Type to filter": [
null,
"Escreva para filtrar"
]
}
}
};
if (typeof define === 'function' && define.amd) {
define("pt_BR", ['jed'], function () {
return factory(new Jed(translations));
});
} else {
if (!window.locales) {
window.locales = {};
}
window.locales.pt_BR = factory(new Jed(translations));
}
}(this, function (i18n) {
return i18n;
})
);

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

View File

@ -9,7 +9,7 @@
</head>
<body class="reset">
<div id="conversejs" class="fullscreen">
<div id="conversejs" class="fullscreen converse-fullscreen">
<div class="sidebar"></div>
<div class="converse-chatboxes row no-gutters">
<div id="controlbox" class="chatbox">
@ -42,17 +42,24 @@
<div class="message chat-info chat-error">This is an error message</div>
<div class="message chat-msg">
<canvas data-avatar="/mockup/images/romeo.jpg" height="36" width="36" class="avatar"></canvas>
<div class="chat-msg-content">
<span class="chat-msg-heading">
<span class="chat-msg-author">Romeo Montague</span>
<span class="chat-msg-time">15:31</span>
<canvas data-avatar="/mockup/images/romeo.jpg" height="36" width="36" class="avatar chat-msg__avatar"></canvas>
<div class="chat-msg__content">
<span class="chat-msg__heading">
<span class="chat-msg__author">Romeo Montague</span>
<span class="chat-msg__time">15:31</span>
</span>
<span class="chat-msg-text">
<div class="chat-msg__body">
<div class="chat-msg__message">
<span class="chat-msg__text">
He jests at scars that never felt a wound.
</span>
</div>
</div>
</div>
<div class="chat-msg__actions">
<button class="chat-msg__action fa fa-pencil" title="Edit this message">&nbsp;</button>
</div>
</div>
<div class="message date-separator">
<hr class="separator">
@ -60,54 +67,77 @@
</div>
<div class="message chat-msg">
<canvas data-avatar="/mockup/images/romeo.jpg" height="36" width="36" class="avatar"></canvas>
<div class="chat-msg-content">
<span class="chat-msg-heading">
<span class="chat-msg-author">Romeo Montague</span>
<span class="chat-msg-time">15:31</span>
<canvas data-avatar="/mockup/images/romeo.jpg" height="36" width="36" class="avatar chat-msg__avatar"></canvas>
<div class="chat-msg__content">
<span class="chat-msg__heading">
<span class="chat-msg__author">Romeo Montague</span>
<span class="chat-msg__time">15:31</span>
</span>
<span class="chat-msg-text">
<div class="chat-msg__body">
<div class="chat-msg__message">
<span class="chat-msg__text">
But soft, what light through yonder window breaks?
</span>
</div>
</div>
</div>
<div class="chat-msg__actions">
<button class="chat-msg__action fa fa-pencil" title="Edit this message">&nbsp;</button>
</div>
</div>
<div class="message chat-msg chat-msg-followup">
<canvas data-avatar="/mockup/images/romeo.jpg" height="36" width="36" class="avatar"></canvas>
<div class="chat-msg-content">
<span class="chat-msg-heading">
<span class="chat-msg-author">Romeo Montague</span>
<span class="chat-msg-time">15:31</span>
<canvas data-avatar="/mockup/images/romeo.jpg" height="36" width="36" class="avatar chat-msg__avatar"></canvas>
<div class="chat-msg__content">
<span class="chat-msg__heading">
<span class="chat-msg__author">Romeo Montague</span>
<span class="chat-msg__time">15:31</span>
</span>
<span class="chat-msg-text">
<div class="chat-msg__body">
<div class="chat-msg__message">
<span class="chat-msg__text">
It is the east and Juliet is the sun!
</span>
</div>
</div>
</div>
<div class="chat-msg__actions">
<button class="chat-msg__action fa fa-pencil" title="Edit this message">&nbsp;</button>
</div>
</div>
<div class="message chat-msg chat-msg-followup">
<canvas data-avatar="/mockup/images/romeo.jpg" height="36" width="36" class="avatar"></canvas>
<div class="chat-msg-content">
<span class="chat-msg-heading">
<span class="chat-msg-author">Romeo Montague</span>
<span class="chat-msg-time">15:31</span>
<canvas data-avatar="/mockup/images/romeo.jpg" height="36" width="36" class="avatar chat-msg__avatar"></canvas>
<div class="chat-msg__content">
<span class="chat-msg__heading">
<span class="chat-msg__author">Romeo Montague</span>
<span class="chat-msg__time">15:31</span>
</span>
<span class="chat-msg-text">
<div class="chat-msg__body">
<div class="chat-msg__message">
<span class="chat-msg__text">
Arise, fair sun, and kill the envious moon,
</span>
</div>
</div>
</div>
<div class="chat-msg__actions">
<button class="chat-msg__action fa fa-pencil" title="Edit this message">&nbsp;</button>
</div>
</div>
<div class="message chat-info chat-event">Romeo Montague is busy</div>
<div class="message chat-msg">
<canvas height="36" width="36" class="avatar"></canvas>
<div class="chat-msg-content">
<span class="chat-msg-heading">
<span class="chat-msg-author">Juliet Capulet</span>
<span class="chat-msg-time">15:31</span>
<canvas height="36" width="36" class="avatar chat-msg__avatar"></canvas>
<div class="chat-msg__content">
<span class="chat-msg__heading">
<span class="chat-msg__author">Juliet Capulet</span>
<span class="chat-msg__time">15:31</span>
</span>
<span class="chat-msg-text">
<div class="chat-msg__body">
<div class="chat-msg__message">
<span class="chat-msg__text">
O Romeo, Romeo! wherefore art thou Romeo?
Deny thy father and refuse thy name;
Or, if thou wilt not, be but sworn my love,
@ -116,6 +146,11 @@
</div>
</div>
</div>
<div class="chat-msg__actions">
<button class="chat-msg__action fa fa-pencil" title="Edit this message">&nbsp;</button>
</div>
</div>
</div>
<form class="sendXMPPMessage">
<ul class="chat-toolbar no-text-select">

View File

@ -44,21 +44,31 @@
<div class="message chat-info chat-event" data-isodate="2018-04-36T18:07:36+02:00" data-join="&quot;Romeo Montague&quot;">
Romeo Montague has entered the room</div>
<div class="message chat-msg chat-action" data-isodate="2018-04-36T18:07:38+02:00">
<span class="chat-msg-heading">
<span class="chat-msg-author">**Romeo Montague</span>
<div class="message chat-msg chat-msg--action" data-isodate="2018-04-36T18:07:38+02:00">
<div class="chat-msg__content chat-msg__content--action">
<span class="chat-msg__heading">
<time timestamp="2018-12-29" class="chat-msg__time">15:29</time>
<span class="chat-msg__author">**Romeo Montague</span>
</span>
<span class="chat-msg-text">looks around</span>
<span class="chat-msg__text">looks around</span>
</div>
</div>
<div class="message chat-msg">
<canvas data-avatar="/mockup/images/romeo.jpg" height="36" width="36" class="avatar"></canvas>
<div class="chat-msg-content">
<span class="chat-msg-heading">
<span class="chat-msg-author">Romeo Montague <span class="badge badge-primary">Developer</span></span>
<span class="chat-msg-time">15:31</span>
</span>
<span class="chat-msg-text">He jests at scars that never felt a wound.</span>
<canvas class="avatar chat-msg__avatar" data-avatar="/mockup/images/romeo.jpg" height="36" width="36"></canvas>
<div class="chat-msg__content">
<div class="chat-msg__heading">
<span class="chat-msg__author">Romeo Montague <span class="badge badge-primary">Developer</span></span>
<span class="chat-msg__time">15:31</span>
</div>
<div class="chat-msg__body">
<div class="chat-msg__message">
<div class="chat-msg__text">He jests at scars that never felt a wound.</div>
</div>
<div class="chat-msg__actions">
<button class="chat-msg__action fa fa-pencil" title="Edit this message">&nbsp;</button>
</div>
</div>
</div>
</div>
@ -71,76 +81,106 @@
Juliet has entered the room</div>
<div class="message chat-msg">
<canvas data-avatar="/mockup/images/romeo.jpg" height="36" width="36" class="avatar"></canvas>
<div class="chat-msg-content">
<span class="chat-msg-heading">
<span class="chat-msg-author">Romeo Montague</span>
<span class="chat-msg-time">19:36</span>
</span>
<span class="chat-msg-text">
But, soft! what light through yonder window breaks?
</span>
<canvas class="avatar chat-msg__avatar" data-avatar="/mockup/images/romeo.jpg" height="36" width="36"></canvas>
<div class="chat-msg__content">
<div class="chat-msg__heading">
<span class="chat-msg__author">Romeo Montague</span>
<span class="chat-msg__time">19:36</span>
</div>
<div class="chat-msg__body">
<div class="chat-msg__message">
<div class="chat-msg__text">But, soft! what light through yonder window breaks?</div>
</div>
<div class="chat-msg__actions">
<button class="chat-msg__action fa fa-pencil" title="Edit this message">&nbsp;</button>
</div>
</div>
<div class="message chat-msg chat-msg-followup">
<canvas data-avatar="/mockup/images/romeo.jpg" height="36" width="36" class="avatar"></canvas>
<div class="chat-msg-content">
<span class="chat-msg-heading">
<span class="chat-msg-author">Romeo Montague</span>
<span class="chat-msg-time">19:36</span>
</span>
<span class="chat-msg-text">It is the east, and Juliet is the sun.</span>
</div>
</div>
<div class="message chat-msg chat-msg-followup">
<canvas data-avatar="/mockup/images/romeo.jpg" height="36" width="36" class="avatar"></canvas>
<div class="chat-msg-content">
<span class="chat-msg-heading">
<span class="chat-msg-author">Romeo Montague</span>
<span class="chat-msg-time">19:36</span>
</span>
<span class="chat-msg-text">Arise, fair sun, and kill the envious moon, Who is already sick and pale with grief</span>
<div class="message chat-msg chat-msg--followup">
<canvas class="avatar chat-msg__avatar" data-avatar="/mockup/images/romeo.jpg" height="36" width="36"></canvas>
<div class="chat-msg__content">
<div class="chat-msg__heading">
<span class="chat-msg__author">Romeo Montague</span>
<span class="chat-msg__time">19:36</span>
</div>
<div class="chat-msg__body">
<div class="chat-msg__message">
<div class="chat-msg__text">It is the east, and Juliet is the sun.</div>
</div>
<div class="chat-msg__actions">
<button class="chat-msg__action fa fa-pencil" title="Edit this message">&nbsp;</button>
</div>
</div>
</div>
</div>
<div class="message chat-msg chat-msg--followup">
<canvas class="avatar chat-msg__avatar" data-avatar="/mockup/images/romeo.jpg" height="36" width="36"></canvas>
<div class="chat-msg__content">
<div class="chat-msg__heading">
<span class="chat-msg__author">Romeo Montague</span>
<span class="chat-msg__time">19:36</span>
</div>
<div class="chat-msg__body">
<div class="chat-msg__message">
<div class="chat-msg__text">Arise, fair sun, and kill the envious moon, Who is already sick and pale with grief</div>
</div>
<div class="chat-msg__actions">
<button class="chat-msg__action fa fa-pencil" title="Edit this message">&nbsp;</button>
</div>
</div>
</div>
</div>
<div class="message chat-msg">
<canvas height="36" width="36" class="avatar"></canvas>
<div class="chat-msg-content">
<span class="chat-msg-heading">
<span class="chat-msg-author">Juliet Capulet</span>
<span class="chat-msg-time">19:43</span>
</span>
<span class="chat-msg-text">
<canvas class="avatar chat-msg__avatar" height="36" width="36"></canvas>
<div class="chat-msg__content">
<div class="chat-msg__heading">
<span class="chat-msg__author">Juliet Capulet</span>
<span class="chat-msg__time">19:43</span>
</div>
<div class="chat-msg__body">
<div class="chat-msg__message">
<div class="chat-msg__text">
O Romeo, Romeo! wherefore art thou Romeo?
Deny thy father and refuse thy name;
Or, if thou wilt not, be but sworn my love,
And I'll no longer be a Capulet.
</span>
<div class="chat-msg-media"></div>
</div>
</div>
<div class="chat-msg__actions">
<button class="chat-msg__action fa fa-pencil" title="Edit this message">&nbsp;</button>
</div>
</div>
</div>
</div>
<div class="message chat-msg">
<canvas data-avatar="/mockup/images/romeo.jpg" height="36" width="36" class="avatar"></canvas>
<div class="chat-msg-content">
<span class="chat-msg-heading">
<span class="chat-msg-author">Romeo Montague</span>
<span class="chat-msg-time">19:36</span>
</span>
<span class="chat-msg-text">Uploading file: <strong>juliet.jpg</strong>, 120kb</span>
<canvas class="avatar chat-msg__avatar" data-avatar="/mockup/images/romeo.jpg" height="36" width="36"></canvas>
<div class="chat-msg__content">
<div class="chat-msg__heading">
<span class="chat-msg__author">Romeo Montague</span>
<span class="chat-msg__time">19:36</span>
</div>
<div class="chat-msg__body">
<div class="chat-msg__message">
<span class="chat-msg__text">Uploading file: <strong>juliet.jpg</strong>, 120kb</span>
<progress value="50" max="100"/>
</div>
</div>
</div>
</div>
<div class="message chat-msg">
<canvas height="36" width="36" class="avatar"></canvas>
<div class="chat-msg-content">
<span class="chat-msg-heading">
<span class="chat-msg-author">Juliet Capulet</span>
<span class="chat-msg-time">19:45</span>
</span>
<span class="chat-msg-text"></span>
<div class="chat-msg-media">
<canvas class="avatar chat-msg__avatar" height="36" width="36"></canvas>
<div class="chat-msg__content">
<div class="chat-msg__heading">
<span class="chat-msg__author">Juliet Capulet</span>
<span class="chat-msg__time">19:45</span>
</div>
<div class="chat-msg__body">
<div class="chat-msg__message">
<div class="chat-msg__media">
<a href="https://images.unsplash.com/photo-1496660067708-010ebdd7ce72?ixlib=rb-0.3.5&ixid=eyJhcHBfaWQiOjEyMDd9&s=ea3514e6e00d8ce25c24d992b97929d9&dpr=1&auto=format&fit=crop&w=1000&q=80&cs=tinysrgb"
target="_blank" rel="noopener">
<img class="chat-image img-thumbnail" src="https://images.unsplash.com/photo-1496660067708-010ebdd7ce72?ixlib=rb-0.3.5&ixid=eyJhcHBfaWQiOjEyMDd9&s=ea3514e6e00d8ce25c24d992b97929d9&dpr=1&auto=format&fit=crop&w=1000&q=80&cs=tinysrgb">
@ -148,67 +188,98 @@
</div>
</div>
</div>
</div>
</div>
<div class="message chat-msg" data-isodate="2018-04-36T18:07:36+02:00" data-msgid="some-long-id">
<canvas data-avatar="/mockup/images/romeo.jpg" height="36" width="36" class="avatar"></canvas>
<div class="chat-msg-content">
<span class="chat-msg-heading">
<span class="chat-msg-author">Romeo Montague</span>
<span class="chat-msg-time">19:36</span>
<canvas class="avatar chat-msg__avatar" data-avatar="/mockup/images/romeo.jpg" height="36" width="36"></canvas>
<div class="chat-msg__content">
<span class="chat-msg__heading">
<span class="chat-msg__author">Romeo Montague</span>
<span class="chat-msg__time">19:36</span>
</span>
<div>
<div class="chat-msg__body">
<div class="chat-msg__message">
<div class="chat-msg__spoiler-hint">
<span class="spoiler-hint">By a name</span>
<a class="badge badge-info spoiler-toggle" data-toggle-state="closed" href="#"><i class="fa fa-eye"></i>Show more</a>
</div>
<div class="chat-msg-text spoiler collapsed">
<div class="chat-msg__text spoiler collapsed">
I know not how to tell thee who I am: My name, dear saint, is hateful to
myself, Because it is an enemy to thee. Had I it written, I would tear the word.
</div>
<div class="chat-msg-media"></div>
</div>
<div class="chat-msg__actions">
<button class="chat-msg__action fa fa-pencil" title="Edit this message">&nbsp;</button>
</div>
</div>
</div>
</div>
<div class="message chat-info chat-event" data-isodate="2018-03-07T10:21:09+01:00" data-join="&quot;Mercutio&quot;">Mercutio has entered the room</div>
<div class="message chat-info chat-event" data-isodate="2018-03-07T10:21:09+01:00" data-join="&quot;Mercutio&quot;">Topic set by Mercutio</div>
<div class="message chat-info chat-topic" data-isodate="2018-03-07T10:21:09+01:00">Converse.js: The latest release is 3.3.4. Please be
patient if your questions aren't answered immediately. We're all in different timezones.</div>
<div class="message chat-info chat-topic" data-isodate="2018-03-07T10:21:09+01:00">
Converse.js: The latest release is 3.3.4. Please be patient if your questions aren't answered immediately. We're all in different timezones.
</div>
<div class="message chat-msg">
<canvas height="36" width="36" class="avatar"></canvas>
<div class="chat-msg-content">
<span class="chat-msg-heading">
<span class="chat-msg-author">Mercutio</span>
<span class="chat-msg-time">19:49</span>
</span>
<span class="chat-msg-text">I mean, sir, in delay We waste our lights in vain, like lamps by day.</span>
<canvas class="avatar chat-msg__avatar" height="36" width="36"></canvas>
<div class="chat-msg__content">
<div class="chat-msg__heading">
<span class="chat-msg__author">Mercutio</span>
<span class="chat-msg__time">19:49</span>
</div>
<div class="chat-msg__body">
<i title="This message has been edited" class="fa fa-edit chat-msg__edit-modal"></i>
<div class="chat-msg__message">
<span class="chat-msg__text">I mean, sir, in delay We waste our lights in vain, like lamps by day.</span>
</div>
<div class="chat-msg__actions">
<button class="chat-msg__action fa fa-pencil" title="Edit this message">&nbsp;</button>
</div>
</div>
</div>
</div>
<div class="message chat-msg chat-msg-followup">
<canvas height="36" width="36" class="avatar"></canvas>
<div class="chat-msg-content">
<span class="chat-msg-heading">
<span class="chat-msg-author">Mercutio</span>
<span class="chat-msg-time">19:49</span>
</span>
<span class="chat-msg-text">
<div class="message chat-msg chat-msg--followup">
<canvas class="avatar chat-msg__avatar" height="36" width="36"></canvas>
<div class="chat-msg__content">
<div class="chat-msg__heading">
<span class="chat-msg__author">Mercutio</span>
<span class="chat-msg__time">19:49</span>
</div>
<div class="chat-msg__body">
<div class="chat-msg__message">
<span class="chat-msg__text">
Take our good meaning, for our judgment sits.
Five times in that ere once in our five wits.</span>
</div>
<div class="chat-msg__actions">
<button class="chat-msg__action fa fa-pencil" title="Edit this message">&nbsp;</button>
</div>
</div>
</div>
</div>
<div class="message chat-msg chat-msg-followup">
<canvas height="36" width="36" class="avatar"></canvas>
<div class="chat-msg-content">
<span class="chat-msg-heading">
<span class="chat-msg-author">Mercutio</span>
<span class="chat-msg-time">19:49</span>
<div class="message chat-msg chat-msg--followup">
<canvas class="avatar chat-msg__avatar" height="36" width="36"></canvas>
<div class="chat-msg__content">
<span class="chat-msg__heading">
<span class="chat-msg__author">Mercutio</span>
<span class="chat-msg__time">19:49</span>
</span>
<span class="chat-msg-text">
<div class="chat-msg__body">
<div class="chat-msg__message">
<span class="chat-msg__text">
True, I talk of dreams, Which are the children of an idle brain, Begot of nothing but vain fantasy,
Which is as thin of substance as the air And more inconstant than the wind, who wooes
Even now the frozen bosom of the north, And, being anger'd, puffs away from thence,
Turning his face to the dew-dropping south.</span>
</div>
<div class="chat-msg__actions">
<button class="chat-msg__action fa fa-pencil" title="Edit this message">&nbsp;</button>
</div>
</div>
</div>
</div>

View File

@ -35,24 +35,24 @@
<span class="w-100 controlbox-heading">Group Chats</span>
<a class="chatbox-btn fa fa-users" title="Click to add a new room" data-toggle="modal" data-target="#chatroomsModal"></a>
</div>
<div class="list-container rooms-list-container">
<div class="rooms-list items-list">
<div class="controlbox-padded list-item available-chatroom d-flex flex-row">
<a class="open-room available-room w-100" data-room-jid="public@conference.test.com" title="Click to open this room" href="chatroom.html">Capulet's orchard</a>
<div class="list-container items-list-container">
<div class="items-list">
<div class="controlbox-padded list-item available-chatroom d-flex flex-row open">
<a class="list-item-link open-room available-room w-100" data-room-jid="public@conference.test.com" title="Click to open this room" href="chatroom.html">Capulet's orchard</a>
<!-- <span class="badge badge-info msgs-indicator">1</span> -->
<a href="#"
class="room-info fa fa-info-circle" data-container="body" data-toggle="popover" title="Room info"
class="list-item-action room-info fa fa-info-circle" data-container="body" data-toggle="popover" title="Room info"
data-html="true" data-content="<b>Room Address (JID): </b><br>public@conference.test.com<br><b>Discussions: </b><br>Public discussions<br><b>Participants: </b>1<br><b>Features: </b><br>Non-anonymous">
&nbsp;</a>
<a href="#" class="fa fa-bookmark" title="Click to bookmark this room">&nbsp;</a>
<a href="#" class="list-item-action fa fa-bookmark" title="Click to bookmark this room">&nbsp;</a>
</div>
<div class="controlbox-padded list-item available-chatroom d-flex flex-row">
<a class="open-room available-room w-100" data-room-jid="team@conference.test.com" title="Click to open this room" href="chatroom.html">Juliet's Balcony</a>
<a class="list-item-link open-room available-room w-100" data-room-jid="team@conference.test.com" title="Click to open this room" href="chatroom.html">Juliet's Balcony</a>
<a href="#"
class="room-info fa fa-info-circle" data-container="body" data-toggle="popover" title="Room info"
class="list-item-action room-info fa fa-info-circle" data-container="body" data-toggle="popover" title="Room info"
data-html="true" data-content="<b>Room Address (JID): </b><br>public@conference.test.com<br><b>Discussions: </b><br>Public discussions<br><b>Participants: </b>1<br><b>Features: </b><br>Non-anonymous">
&nbsp;</a>
<a href="#" class="fa fa-bookmark" title="Click to bookmark this room">&nbsp;</a>
<a href="#" class="list-item-action fa fa-bookmark" title="Click to bookmark this room">&nbsp;</a>
</div>
</div>
</div>
@ -62,24 +62,24 @@
<div class="d-flex controlbox-padded">
<span class="w-100 controlbox-heading">Bookmarks</span>
</div>
<div class="list-container rooms-list-container">
<div class="rooms-list items-list">
<div class="list-container items-list-container">
<div class="items-list">
<div class="controlbox-padded list-item available-chatroom d-flex flex-row">
<a class="open-room available-room w-100" data-room-jid="public@conference.test.com" title="Click to open this room" href="chatroom.html">Capulet's orchard</a>
<a class="list-item-link open-room available-room w-100" data-room-jid="public@conference.test.com" title="Click to open this room" href="chatroom.html">Capulet's orchard</a>
<!-- <span class="badge badge-info msgs-indicator">1</span> -->
<a href="#"
class="room-info fa fa-info-circle" data-container="body" data-toggle="popover" title="Room info"
class="list-item-action room-info fa fa-info-circle" data-container="body" data-toggle="popover" title="Room info"
data-html="true" data-content="<b>Room Address (JID): </b><br>public@conference.test.com<br><b>Discussions: </b><br>Public discussions<br><b>Participants: </b>1<br><b>Features: </b><br>Non-anonymous">
&nbsp;</a>
<a href="#" class="fa fa-bookmark" title="Click to bookmark this room">&nbsp;</a>
<a href="#" class="list-item-action fa fa-bookmark" title="Click to bookmark this room">&nbsp;</a>
</div>
<div class="controlbox-padded list-item available-chatroom d-flex flex-row">
<a class="open-room available-room w-100" data-room-jid="team@conference.test.com" title="Click to open this room" href="chatroom.html">Juliet's Balcony</a>
<a class="list-item-link open-room available-room w-100" data-room-jid="team@conference.test.com" title="Click to open this room" href="chatroom.html">Juliet's Balcony</a>
<a href="#"
class="room-info fa fa-info-circle" data-container="body" data-toggle="popover" title="Room info"
class="list-item-action room-info fa fa-info-circle" data-container="body" data-toggle="popover" title="Room info"
data-html="true" data-content="<b>Room Address (JID): </b><br>public@conference.test.com<br><b>Discussions: </b><br>Public discussions<br><b>Participants: </b>1<br><b>Features: </b><br>Non-anonymous">
&nbsp;</a>
<a href="#" class="fa fa-bookmark" title="Click to bookmark this room">&nbsp;</a>
<a href="#" class="list-item-action fa fa-bookmark" title="Click to bookmark this room">&nbsp;</a>
</div>
</div>
</div>
@ -108,13 +108,13 @@
<div class="roster-group" id="xmpp-contact-requests">
<a href="#" class="group-toggle controlbox-padded " title="Click to hide these contacts">
<span class="fa fa-caret-down"></span> Contact Requests</a>
<ul class="roster-group-contacts">
<li class=" controlbox-padded offline requesting-xmpp-contact d-flex">
<ul class="items-list roster-group-contacts">
<li class="list-item controlbox-padded offline requesting-xmpp-contact d-flex">
<span class="req-contact-name w-100">The Nurse</span>
<a class="accept-xmpp-request fa fa-check" title="Click here to accept this contact's request" href="#"></a>
<a class="decline-xmpp-request fa fa-times" title="Click here to decline this contact's request" href="#"></a>
</li>
<li class=" controlbox-padded offline requesting-xmpp-contact d-flex">
<li class="list-item controlbox-padded offline requesting-xmpp-contact d-flex">
<span class="req-contact-name w-100">Friar Laurence</span>
<a class="accept-xmpp-request fa fa-check" title="Click here to accept this contact's request" href="#"></a>
<a class="decline-xmpp-request fa fa-times" title="Click here to decline this contact's request" href="#"></a>
@ -125,16 +125,16 @@
<div class="roster-group" data-group="Colleagues">
<a href="#" data-group="Colleagues" class="group-toggle controlbox-padded " title="Click to hide these contacts">
<span class="fa fa-caret-down"></span> Colleagues</a>
<ul>
<li class=" controlbox-padded away current-xmpp-contact d-flex">
<ul class="items-list roster-group-contacts">
<li class="list-item controlbox-padded away current-xmpp-contact d-flex">
<a class="open-chat w-100" title="Click to chat with this contact" href="chatbox.html">
<span class="fa fa-circle-o" title="this contact is away"></span> Balthasar</a>
<a class="remove-xmpp-contact fa fa-trash" title="Click to remove this contact" href="#"></a>
<a class="list-item-action remove-xmpp-contact fa fa-trash" title="Click to remove this contact" href="#"></a>
</li>
<li class=" controlbox-padded dnd current-xmpp-contact d-flex">
<li class="list-item controlbox-padded dnd current-xmpp-contact d-flex">
<a class="open-chat w-100" title="Click to chat with this contact" href="chatbox.html">
<span class="fa fa-minus-circle" title="This contact is busy"></span> Escalus, Prince of Verona</a>
<a class="remove-xmpp-contact fa fa-trash" title="Click to remove this contact" href="#"></a>
<a class="list-item-action remove-xmpp-contact fa fa-trash" title="Click to remove this contact" href="#"></a>
</li>
</li>
</div>
@ -142,21 +142,21 @@
<div class="roster-group" data-group="Family">
<a href="#" data-group="Family" class="group-toggle controlbox-padded " title="Click to hide these contacts">
<span class="fa fa-caret-down"></span> Family</a>
<ul>
<li class=" controlbox-padded online current-xmpp-contact d-flex">
<ul class="items-list roster-group-contacts">
<li class="list-item controlbox-padded online current-xmpp-contact d-flex">
<a class="open-chat w-100" title="Click to chat with this contact" href="chatbox.html">
<span class="fa fa-circle" title="This contact is online"></span> Benvolio Montague</a>
<a class="remove-xmpp-contact fa fa-trash" title="Click to remove this contact" href="#"></a>
<a class="list-item-action remove-xmpp-contact fa fa-trash" title="Click to remove this contact" href="#"></a>
</li>
<li class=" controlbox-padded offline current-xmpp-contact d-flex">
<li class="list-item controlbox-padded offline current-xmpp-contact d-flex">
<a class="open-chat w-100" title="Click to chat with this contact" href="chatbox.html">
<span class="fa fa-times-circle" title="This contact is offline"></span> Montague</a>
<a class="remove-xmpp-contact fa fa-trash" title="Click to remove this contact" href="#"></a>
<a class="list-item-action remove-xmpp-contact fa fa-trash" title="Click to remove this contact" href="#"></a>
</li>
<li class=" controlbox-padded offline current-xmpp-contact d-flex">
<li class="list-item controlbox-padded offline current-xmpp-contact d-flex">
<a class="open-chat w-100" title="Click to chat with this contact" href="chatbox.html">
<span class="fa fa-times-circle" title="This contact is offline"></span> Lady Montague</a>
<a class="remove-xmpp-contact fa fa-trash" title="Click to remove this contact" href="#"></a>
<a class="list-item-action remove-xmpp-contact fa fa-trash" title="Click to remove this contact" href="#"></a>
</li>
</ul>
</div>
@ -164,11 +164,11 @@
<div class="roster-group" data-group="Friends">
<a href="#" data-group="Friends" class="group-toggle controlbox-padded " title="Click to hide these contacts">
<span class="fa fa-caret-down"></span> Friends</a>
<ul>
<li class=" controlbox-padded online current-xmpp-contact d-flex">
<ul class="items-list roster-group-contacts">
<li class="list-item controlbox-padded online current-xmpp-contact d-flex">
<a class="open-chat w-100" title="Click to chat with this contact" href="chatbox.html">
<span class="fa fa-circle" title="This contact is online"></span> Mercutio</a>
<a class="remove-xmpp-contact fa fa-trash" title="Click to remove this contact" href="#"></a>
<a class="list-item-action remove-xmpp-contact fa fa-trash" title="Click to remove this contact" href="#"></a>
</li>
</ul>
</div>
@ -176,21 +176,21 @@
<div class="roster-group" data-group="Ungrouped">
<a href="#" class="group-toggle controlbox-padded " title="Click to hide these contacts">
<span class="fa fa-caret-down"></span> Ungrouped</a>
<ul>
<li class=" controlbox-padded online current-xmpp-contact d-flex">
<ul class="items-list roster-group-contacts">
<li class="list-item controlbox-padded online current-xmpp-contact d-flex">
<a class="open-chat w-100" title="Click to chat with this contact" href="chatbox.html">
<span class="fa fa-circle" title="This contact is online"></span> Gregory</a>
<a class="remove-xmpp-contact fa fa-trash" title="Click to remove this contact" href="#"></a>
<a class="list-item-action remove-xmpp-contact fa fa-trash" title="Click to remove this contact" href="#"></a>
</li>
<li class=" controlbox-padded online current-xmpp-contact d-flex">
<li class="list-item controlbox-padded online current-xmpp-contact d-flex">
<a class="open-chat w-100" title="Click to chat with this contact" href="chatbox.html">
<span class="fa fa-circle" title="This contact is online"></span> Peter</a>
<a class="remove-xmpp-contact fa fa-trash" title="Click to remove this contact" href="#"></a>
<a class="list-item-action remove-xmpp-contact fa fa-trash" title="Click to remove this contact" href="#"></a>
</li>
<li class=" controlbox-padded online current-xmpp-contact d-flex">
<li class="list-item controlbox-padded online current-xmpp-contact d-flex">
<a class="open-chat w-100" title="Click to chat with this contact" href="chatbox.html">
<span class="fa fa-circle" title="This contact is online"></span> Sampson</a>
<a class="remove-xmpp-contact fa fa-trash" title="Click to remove this contact" href="#"></a>
<a class="list-item-action remove-xmpp-contact fa fa-trash" title="Click to remove this contact" href="#"></a>
</li>
</ul>
</div>
@ -198,14 +198,14 @@
<div class="roster-group" id="pending-xmpp-contacts">
<a href="#" class="group-toggle controlbox-padded " title="Click to hide these contacts">
<span class="fa fa-caret-down"></span> Pending Contacts</a>
<ul>
<li class=" controlbox-padded offline pending-xmpp-contact d-flex">
<ul class="items-list roster-group-contacts">
<li class="list-item controlbox-padded offline pending-xmpp-contact d-flex">
<span class="pending-contact-name w-100">An Apothecary</span>
<a class="remove-xmpp-contact fa fa-trash" title="Click to remove this contact" href="#"></a>
<a class="list-item-action remove-xmpp-contact fa fa-trash" title="Click to remove this contact" href="#"></a>
</li>
<li class=" controlbox-padded offline pending-xmpp-contact d-flex">
<li class="list-item controlbox-padded offline pending-xmpp-contact d-flex">
<span class="pending-contact-name w-100">Abram</span>
<a class="remove-xmpp-contact fa fa-trash" title="Click to remove this contact" href="#"></a>
<a class="list-item-action remove-xmpp-contact fa fa-trash" title="Click to remove this contact" href="#"></a>
</li>
</ul>
</div>

View File

@ -3,7 +3,7 @@ const u = converse_utils;
window.renderAvatars = function (el) {
el = el || document;
const canvasses = el.querySelectorAll('canvas.avatar');
const canvasses = el.querySelectorAll('canvas.chat-msg__avatar');
_.each(canvasses, (canvas_el) => {
const avatar_url = canvas_el.getAttribute('data-avatar');
if (!avatar_url) {

View File

@ -1,7 +1,16 @@
#conversejs {
#conversejs.fullscreen {
#controlbox {
#chatrooms {
.bookmarks-list {
dl.rooms-list.bookmarks {
dd.available-chatroom {
a {
&.open-room {
width: 80%;
}
}
}
}
}
}
}

View File

@ -67,9 +67,8 @@
}
.user-custom-message {
color: white;
color: lighten($chat-head-color, 50%);
font-size: 75%;
font-style: italic;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
@ -127,7 +126,7 @@
background-color: $chat-head-color;
box-shadow: 1px 3px 5px 3px rgba(0, 0, 0, 0.4);
z-index: 1;
overflow-y: scroll;
overflow-y: hidden;
width: 100%;
@media screen and (max-height: $mobile-landscape-height) {
@ -467,21 +466,6 @@
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 {
@ -554,7 +538,7 @@
font-size: $font-size-huge;
padding: 0;
.user-custom-message {
font-size: 50%;
font-size: 70%;
height: auto;
line-height: $line-height;
}
@ -579,7 +563,7 @@
@include make-col-ready();
@include media-breakpoint-up(md) {
@include make-col(9);
@include make-col(8);
}
@include media-breakpoint-up(lg) {
@include make-col(9);
@ -594,26 +578,12 @@
height: $fullpage-chat-height;
min-height: $fullpage-chat-height/2;
width: $fullpage-chat-width;
overflow: hidden;
}
.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;

View File

@ -119,8 +119,12 @@
.mentioned {
font-weight: bold;
}
.disconnect-msg {
padding: 2em 2em 0 2em;
.disconnect-container {
margin: 1em;
width: 100%;
h3.disconnect-msg {
padding-bottom: 1em;
}
}
.chat-area {
display: flex;
@ -245,12 +249,6 @@
font-size: 90%;
color: $error-color;
}
.chatroom-form {
label,
input[type=text] {
display: block;
}
}
input[type=button],
input[type=submit] {
margin: 0 0.5em;

View File

@ -20,7 +20,9 @@
}
}
.set-xmpp-status, .xmpp-status, .roster-contacts {
.set-xmpp-status,
.xmpp-status,
.roster-contacts {
.fa-circle {
color: $green;
}
@ -30,9 +32,7 @@
.fa-dot-circle-o {
color: $orange,
}
.fa-circle-o {
color: $subdued-color;
}
.fa-circle-o,
.fa-times-circle {
color: $subdued-color;
}
@ -350,60 +350,6 @@
}
}
@include media-breakpoint-down(sm) {
#conversejs:not(.converse-embedded) {
left: 0;
right: 0;
padding-left: env(safe-area-inset-left);
padding-right: env(safe-area-inset-right);
.converse-chatboxes {
margin: 0 !important;
flex-direction: row !important;
justify-content: space-between;
.converse-chatroom {
font-size: 14px;
}
.chatbox {
.box-flyout {
margin-left: 15px; // Counteracts Bootstrap margins, but
// not clear why needed...
left: 0;
bottom: 0;
border-radius: 0;
width: 100vw !important;
height: 100vh !important;
}
}
#controlbox {
width: 100vw !important;
.box-flyout {
width: 100vw !important;
height: 100vh !important;
margin-left: 30px;
}
.sidebar {
display: block;
}
}
&.sidebar-open {
.chatbox:not(#controlbox) {
display: none;
}
#controlbox {
.controlbox-pane {
display: block;
}
}
}
}
}
}
#conversejs.converse-overlayed {
#controlbox {
@ -461,8 +407,9 @@
#conversejs.converse-mobile {
#controlbox {
@include make-col-ready();
@include media-breakpoint-up(md) {
@include make-col(3);
@include make-col(4);
}
@include media-breakpoint-up(lg) {
@include make-col(3);
@ -563,3 +510,67 @@
}
}
}
@include media-breakpoint-down(sm) {
#conversejs:not(.converse-embedded) {
left: 0;
right: 0;
padding-left: env(safe-area-inset-left);
padding-right: env(safe-area-inset-right);
.converse-chatboxes {
margin: 0 !important;
flex-direction: row !important;
justify-content: space-between;
.converse-chatroom {
font-size: 14px;
}
.chatbox {
.box-flyout {
margin-left: 15px; // Counteracts Bootstrap margins, but
// not clear why needed...
left: 0;
bottom: 0;
border-radius: 0;
width: 100vw !important;
height: 100vh !important;
}
}
#controlbox {
width: 100vw !important;
.box-flyout {
width: 100vw !important;
height: 100vh !important;
}
.sidebar {
display: block;
}
}
&.sidebar-open {
.chatbox:not(#controlbox) {
display: none;
}
#controlbox {
.controlbox-pane {
display: block;
}
}
}
}
}
#conversejs.converse-overlayed {
.converse-chatboxes {
.chatbox {
.box-flyout {
margin-left: 30px; // Counteracts Bootstrap margins, but
// not clear why needed...
}
}
}
}
}

View File

@ -71,7 +71,7 @@ body.reset {
&.converse-mobile {
.converse-chatboxes {
width: 100vw;
right: 15px; // Hack due to padding added by bootstrap
left: -15px; // Hack due to padding added by bootstrap
}
}
&.converse-overlayed {

118
sass/_lists.scss Normal file
View File

@ -0,0 +1,118 @@
#conversejs {
.list-container {
text-align: left;
padding: 0.3em 0;
.list-toggle {
font-family: $heading-font;
display: block;
color: $text-color;
padding: 0 0 0.5rem 0;
&:hover {
color: $dark-gray-color;
}
}
}
.items-list {
text-align: left;
.list-item {
border: none;
clear: both;
color: $text-color;
display: block;
height: 2em;
overflow: hidden;
padding-top: 0.5em;
text-shadow: 0 1px 0 $text-shadow-color;
word-wrap: break-word;
.list-item-link {
&:hover {
color: $dark-link-color;
}
font-size: $font-size;
line-height: $font-size;
padding-right: 0.5em;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.list-item-action {
opacity: 0;
font-size: $font-size-tiny;
padding: 0;
margin: 0 0 0 0.4em;
width: 1.6em;
&:before {
font-size: $font-size;
}
&.button-on {
color: $link-color;
&:hover {
color: $dark-link-color;
}
}
color: $subdued-color;
&:hover {
color: $gray-color;
opacity: 1;
}
}
&.open {
background-color: $controlbox-head-color;
&:hover {
background-color: $controlbox-head-color !important;
}
a {
color: white;
}
.list-item-link {
&:hover {
color: white;
}
}
.list-item-action {
color: lighten($lightest-blue, 25%);
&:hover {
color: white;
}
}
.fa-circle {
color: lighten($green, 25%);
}
.fa-minus-circle {
color: lighten($red, 15%);
}
.fa-dot-circle-o {
color: lighten($orange, 15%);
}
.fa-circle-o,
.fa-times-circle {
color: lighten($subdued-color, 25%);
}
}
&:hover {
background-color: lighten($controlbox-head-color, 45%);
.fa {
opacity: 1;
}
}
&.unread-msgs {
.msgs-indicator {
border-radius: 10%;
opacity: 1;
}
.available-room,
.open-room {
width: 100%;
font-weight: bold;
}
}
}
}
}

View File

@ -56,7 +56,7 @@
max-height: 15em;
max-width: 100%;
}
&.chat-action {
&.chat-msg--action {
font-style: italic;
}
@ -72,6 +72,19 @@
}
&:hover {
background-color: rgba(0, 0, 0, 0.035);
.chat-msg__actions {
.chat-msg__action {
opacity: 1;
}
}
}
&.correcting {
&.groupchat {
background-color: lighten($chatroom-head-color, 35%);
}
&:not(.groupchat) {
background-color: lighten($chat-head-color, 50%);
}
}
.spoiler {
@ -91,19 +104,46 @@
whitespace: nowrap;
}
}
.chat-msg-content {
.chat-msg__content {
display: flex;
flex-direction: column;
justify-content: space-between;
align-items: stretch;
margin-left: 0.5rem;
width: 100%;
}
.chat-msg__content--action {
margin-left: 0;
}
.chat-msg__body {
display: flex;
flex-direction: row;
justify-content: space-between;
width: 100%;
}
.chat-msg__message {
display: flex;
flex-direction: column;
width: 100%;
}
.chat-msg__edit-modal {
cursor: pointer;
padding-right: 0.5em;
}
&.headline {
.chat-msg-content {
.chat-msg__body {
margin-left: 0;
}
}
.chat-msg-text {
.chat-msg__text {
padding: 0;
color: $message-text-color;
width: 100%;
a {
word-wrap: break-word;
word-break: break-all;
@ -113,8 +153,9 @@
}
}
.chat-msg-media {
.chat-msg__media {
margin-top: 0.25rem;
word-break: break-all;
a {
word-wrap: break-word;
}
@ -123,19 +164,37 @@
}
}
.avatar {
.chat-msg__actions {
.chat-msg__action {
height: $message-font-size;
font-size: $message-font-size;
padding: 0;
border: none;
opacity: 0;
background: transparent;
cursor: pointer;
&:focus {
display: block;
}
}
}
.chat-msg__avatar {
margin-top: 0.5em;
height: 36px;
vertical-align: middle;
width: 36px;
}
.chat-msg-heading {
.chat-msg__heading {
width: 100%;
margin-top: 0.5em;
padding-right: 0.25rem;
padding-bottom: 0.25rem;
display: block;
.chat-msg-author {
.chat-msg__author {
white-space: nowrap;
font-family: $heading-font;
font-size: 115%;
.badge {
@ -143,26 +202,39 @@
font-family: $normal_font;
}
}
.chat-msg-time {
.chat-msg__time {
padding-left: 0.25em;
color: lighten($text-color, 15%);
}
}
&.chat-action {
display: block;
.chat-msg-heading {
float: left;
&.chat-msg--action {
.chat-msg__content {
flex-wrap: wrap;
flex-direction: row;
justify-content: flex-start;
}
.chat-msg__text {
width: auto;
}
.chat-msg__heading {
margin-top: 0;
padding-bottom: 0;
width: auto;
}
.chat-msg__author {
font-size: $message-font-size;
}
.chat-msg__time {
margin-left: 0;
}
}
&.chat-msg-followup {
.chat-msg-heading,
.avatar {
&.chat-msg--followup {
.chat-msg__heading,
.chat-msg__avatar {
display: none;
}
.chat-msg-content {
.chat-msg__content {
margin-left: 2.75rem;
}
}
@ -183,8 +255,8 @@
#conversejs.converse-overlayed {
.message {
&.chat-msg {
&.chat-msg-followup {
.chat-msg-content {
&.chat-msg--followup {
.chat-msg__body {
margin-left: 0;
}
}
@ -196,7 +268,7 @@
#conversejs:not(.converse-embedded) {
.message {
&.chat-msg {
.chat-msg-author {
.chat-msg__author {
white-space: normal;
}
}

View File

@ -1,105 +0,0 @@
#conversejs {
.list-container {
text-align: left;
padding: 0.3em 0;
.rooms-toggle {
font-family: $heading-font;
display: block;
color: $text-color;
padding: 0 0 0.5rem 0;
&:hover {
color: $dark-gray-color;
}
}
.items-list {
text-align: left;
.list-item {
border: none;
clear: both;
color: $text-color;
display: block;
height: 2em;
overflow: hidden;
padding-top: 0.5em;
text-shadow: 0 1px 0 $text-shadow-color;
word-wrap: break-word;
}
.available-chatroom,
.open-headline,
.open-chatroom {
&:hover {
background-color: lighten($controlbox-head-color, 45%);
a.add-bookmark,
a.room-info {
display: block !important;
}
}
&.unread-msgs {
.msgs-indicator {
border-radius: 10%;
opacity: 1;
}
.available-room,
.open-room {
width: 100%;
font-weight: bold;
}
}
a {
&:hover {
color: $dark-link-color;
}
&.add-bookmark,
&.room-info {
display: none;
&:before {
font-size: 15px;
}
}
&.open-room {
width: 68%;
float: left;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
padding-right: 0.5em;
}
&.available-room {
width: 85%;
}
}
.add-bookmark,
.remove-bookmark {
&.button-on {
color: $link-color;
&:hover {
color: $dark-link-color;
}
}
color: $subdued-color;
}
}
}
}
}
#conversejs.fullscreen {
#controlbox {
#chatrooms {
.bookmarks-list {
dl.rooms-list.bookmarks {
dd.available-chatroom {
a {
&.open-room {
width: 80%;
}
}
}
}
}
}
}
}

View File

@ -72,17 +72,6 @@
}
li {
border: none;
clear: both;
color: $text-color;
display: block;
overflow-y: hidden;
text-shadow: 0 1px 0 $text-shadow-color;
line-height: $font-size;
width: 100%;
height: 2em;
padding-top: 0.5em;
&.requesting-xmpp-contact {
a {
line-height: $line-height;
@ -94,14 +83,6 @@
padding: 0 0.2em 0 0;
}
}
a {
&:hover {
color: $dark-link-color;
}
.fa:hover {
color: white;
}
}
.open-chat {
margin: 0;
@ -128,7 +109,7 @@
text-overflow: ellipsis;
padding: 0;
margin: 0;
max-width: 80%;
max-width: 90%;
float: none;
height: 100%;
&.unread-msgs {
@ -156,22 +137,9 @@
white-space: nowrap;
text-overflow: ellipsis;
}
span {
padding: 0;
}
.decline-xmpp-request {
margin-left: 5px;
}
.remove-xmpp-contact {
font-size: $font-size-tiny;
margin: 0;
padding: 0;
width: 2em;
display: none;
&:before {
font-size: $font-size;
}
}
&:hover {
background-color: lighten($controlbox-head-color, 45%);
.remove-xmpp-contact {

View File

@ -43,8 +43,8 @@
@import "profile";
@import "chatbox";
@import "controlbox";
@import "roomslist";
@import "roster";
@import "lists";
@import "chatrooms";
@import "headline";
@import "messages";

View File

@ -518,20 +518,28 @@
_converse.connection._dataRecv(test_utils.createRequest(stanza));
test_utils.waitUntil(function () {
return $('#chatrooms div.bookmarks.rooms-list .room-item').length;
return document.querySelectorAll('#chatrooms div.bookmarks.rooms-list .room-item').length;
}, 300).then(function () {
expect($('#chatrooms div.bookmarks.rooms-list .room-item').length).toBe(4);
expect($('#chatrooms div.bookmarks.rooms-list .room-item a').text().trim()).toBe(
"1st Bookmark  Another room  Bookmark with a very very long name that will be shortened  The Play's the Thing")
expect(document.querySelectorAll('#chatrooms div.bookmarks.rooms-list .room-item').length).toBe(4);
const els = document.querySelectorAll('#chatrooms div.bookmarks.rooms-list .room-item a.list-item-link');
expect(els[0].textContent).toBe("1st Bookmark");
expect(els[1].textContent).toBe("Another room");
expect(els[2].textContent).toBe("Bookmark with a very very long name that will be shortened");
expect(els[3].textContent).toBe("The Play's the Thing");
spyOn(window, 'confirm').and.returnValue(true);
$('#chatrooms .bookmarks.rooms-list .room-item:nth-child(2) a:nth-child(2)')[0].click();
document.querySelector('#chatrooms .bookmarks.rooms-list .room-item:nth-child(2) a:nth-child(2)').click();
expect(window.confirm).toHaveBeenCalled();
return test_utils.waitUntil(function () {
return $('#chatrooms .bookmarks.rooms-list .room-item a').text().trim() ===
"1st Bookmark  Bookmark with a very very long name that will be shortened  The Play's the Thing";
return document.querySelectorAll('#chatrooms div.bookmarks.rooms-list .room-item').length === 3;
}, 300)
}).then(done);
}).then(() => {
const els = document.querySelectorAll('#chatrooms div.bookmarks.rooms-list .room-item a.list-item-link');
expect(els[0].textContent).toBe("1st Bookmark");
expect(els[1].textContent).toBe("Bookmark with a very very long name that will be shortened");
expect(els[2].textContent).toBe("The Play's the Thing");
done();
}).catch(_.partial(console.error, _));
});
}));

View File

@ -76,33 +76,33 @@
test_utils.waitUntil(function () {
return u.isVisible(view.el);
}).then(function () {
expect(view.el.querySelectorAll('.chat-action').length).toBe(1);
expect(_.includes(view.el.querySelector('.chat-msg-author').textContent, '**Max Frankfurter')).toBeTruthy();
expect($(view.el).find('.chat-msg-text').text()).toBe(' is tired');
expect(view.el.querySelectorAll('.chat-msg--action').length).toBe(1);
expect(_.includes(view.el.querySelector('.chat-msg__author').textContent, '**Max Frankfurter')).toBeTruthy();
expect($(view.el).find('.chat-msg__text').text()).toBe(' is tired');
message = '/me is as well';
test_utils.sendMessage(view, message);
expect(view.el.querySelectorAll('.chat-action').length).toBe(2);
expect(view.el.querySelectorAll('.chat-msg--action').length).toBe(2);
return test_utils.waitUntil(() => $(view.el).find('.chat-msg-author:last').text() === '**Max Mustermann');
return test_utils.waitUntil(() => $(view.el).find('.chat-msg__author:last').text().trim() === '**Max Mustermann');
}).then(function () {
expect($(view.el).find('.chat-msg-text:last').text()).toBe(' is as well');
expect($(view.el).find('.chat-msg:last').hasClass('chat-msg-followup')).toBe(false);
expect($(view.el).find('.chat-msg__text:last').text()).toBe(' is as well');
expect($(view.el).find('.chat-msg:last').hasClass('chat-msg--followup')).toBe(false);
// Check that /me messages after a normal message don't
// get the 'chat-msg-followup' class.
// get the 'chat-msg--followup' class.
message = 'This a normal message';
test_utils.sendMessage(view, message);
let message_el = view.el.querySelector('.message:last-child');
expect(u.hasClass('chat-msg-followup', message_el)).toBeFalsy();
expect(u.hasClass('chat-msg--followup', message_el)).toBeFalsy();
message = '/me wrote a 3rd person message';
test_utils.sendMessage(view, message);
message_el = view.el.querySelector('.message:last-child');
expect(view.el.querySelectorAll('.chat-action').length).toBe(3);
expect($(view.el).find('.chat-msg-text:last').text()).toBe(' wrote a 3rd person message');
expect($(view.el).find('.chat-msg-author:last').is(':visible')).toBeTruthy();
expect(u.hasClass('chat-msg-followup', message_el)).toBeFalsy();
expect(view.el.querySelectorAll('.chat-msg--action').length).toBe(3);
expect($(view.el).find('.chat-msg__text:last').text()).toBe(' wrote a 3rd person message');
expect($(view.el).find('.chat-msg__author:last').is(':visible')).toBeTruthy();
expect(u.hasClass('chat-msg--followup', message_el)).toBeFalsy();
done();
});
});
@ -635,7 +635,7 @@
spyOn(_converse.connection, 'send');
spyOn(_converse, 'emit');
view.keyPressed({
target: $(view.el).find('textarea.chat-textarea'),
target: view.el.querySelector('textarea.chat-textarea'),
keyCode: 1
});
expect(view.model.get('chat_state')).toBe('composing');
@ -648,7 +648,7 @@
// The notification is not sent again
view.keyPressed({
target: $(view.el).find('textarea.chat-textarea'),
target: view.el.querySelector('textarea.chat-textarea'),
keyCode: 1
});
expect(view.model.get('chat_state')).toBe('composing');
@ -776,7 +776,7 @@
spyOn(view, 'setChatState').and.callThrough();
expect(view.model.get('chat_state')).toBe('active');
view.keyPressed({
target: $(view.el).find('textarea.chat-textarea'),
target: view.el.querySelector('textarea.chat-textarea'),
keyCode: 1
});
expect(view.model.get('chat_state')).toBe('composing');
@ -803,14 +803,14 @@
// out if the user simply types longer than the
// timeout.
view.keyPressed({
target: $(view.el).find('textarea.chat-textarea'),
target: view.el.querySelector('textarea.chat-textarea'),
keyCode: 1
});
expect(view.setChatState).toHaveBeenCalled();
expect(view.model.get('chat_state')).toBe('composing');
view.keyPressed({
target: $(view.el).find('textarea.chat-textarea'),
target: view.el.querySelector('textarea.chat-textarea'),
keyCode: 1
});
expect(view.model.get('chat_state')).toBe('composing');
@ -921,33 +921,25 @@
contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
test_utils.openChatBoxFor(_converse, contact_jid);
view = _converse.chatboxviews.get(contact_jid);
return test_utils.waitUntil(function () {
return view.model.get('chat_state') === 'active';
}, 500);
return test_utils.waitUntil(() => view.model.get('chat_state') === 'active', 500);
}).then(function () {
console.log('chat_state set to active');
view = _converse.chatboxviews.get(contact_jid);
expect(view.model.get('chat_state')).toBe('active');
view.keyPressed({
target: $(view.el).find('textarea.chat-textarea'),
target: view.el.querySelector('textarea.chat-textarea'),
keyCode: 1
});
return test_utils.waitUntil(function () {
return view.model.get('chat_state') === 'composing';
}, 500);
return test_utils.waitUntil(() => view.model.get('chat_state') === 'composing', 500);
}).then(function () {
console.log('chat_state set to composing');
view = _converse.chatboxviews.get(contact_jid);
expect(view.model.get('chat_state')).toBe('composing');
spyOn(_converse.connection, 'send');
return test_utils.waitUntil(function () {
return view.model.get('chat_state') === 'paused';
}, 500);
return test_utils.waitUntil(() => view.model.get('chat_state') === 'paused', 500);
}).then(function () {
console.log('chat_state set to paused');
return test_utils.waitUntil(function () {
return view.model.get('chat_state') === 'inactive';
}, 500);
return test_utils.waitUntil(() => view.model.get('chat_state') === 'inactive', 500);
}).then(function () {
console.log('chat_state set to inactive');
expect(_converse.connection.send).toHaveBeenCalled();
@ -1635,7 +1627,7 @@
return $(view.el).find('.chat-content').find('.chat-msg').length;
}, 1000).then(function () {
expect(view.model.sendMessage).toHaveBeenCalled();
var msg = $(view.el).find('.chat-content').find('.chat-msg').last().find('.chat-msg-text');
var msg = $(view.el).find('.chat-content').find('.chat-msg').last().find('.chat-msg__text');
expect(msg.html()).toEqual(
'<a target="_blank" rel="noopener" href="https://www.openstreetmap.org/?mlat=37.786971&amp;'+
'mlon=-122.399677#map=18/37.786971/-122.399677">https://www.openstreetmap.org/?mlat=37.7869'+

View File

@ -177,7 +177,7 @@
'membersonly': true,
'persistentroom': true,
'publicroom': true,
'roomdesc': 'Welcome to this room',
'roomdesc': 'Welcome to this groupchat',
'whois': 'anyone'
}
});
@ -262,7 +262,7 @@
sent_stanza = sent_IQ_els.pop();
}
expect(sizzle('field[var="muc#roomconfig_roomname"] value', sent_stanza).pop().textContent).toBe('Room');
expect(sizzle('field[var="muc#roomconfig_roomdesc"] value', sent_stanza).pop().textContent).toBe('Welcome to this room');
expect(sizzle('field[var="muc#roomconfig_roomdesc"] value', sent_stanza).pop().textContent).toBe('Welcome to this groupchat');
expect(sizzle('field[var="muc#roomconfig_persistentroom"] value', sent_stanza).pop().textContent).toBe('1');
expect(sizzle('field[var="muc#roomconfig_publicroom"] value ', sent_stanza).pop().textContent).toBe('1');
expect(sizzle('field[var="muc#roomconfig_changesubject"] value', sent_stanza).pop().textContent).toBe('0');
@ -390,7 +390,7 @@
_converse.connection._dataRecv(test_utils.createRequest(presence));
var info_text = view.el.querySelector('.chat-content .chat-info').textContent;
expect(info_text).toBe('A new room has been created');
expect(info_text).toBe('A new groupchat has been created');
// An instant room is created by saving the default configuratoin.
//
@ -453,7 +453,7 @@
}).up()
.c('status', {code: '110'});
_converse.connection._dataRecv(test_utils.createRequest(presence));
expect($chat_content.find('div.chat-info:first').html()).toBe("some1 has entered the room");
expect($chat_content.find('div.chat-info:first').html()).toBe("some1 has entered the groupchat");
presence = $pres({
to: 'dummy@localhost/_converse.js-29092160',
@ -467,7 +467,7 @@
});
_converse.connection._dataRecv(test_utils.createRequest(presence));
expect($chat_content[0].querySelectorAll('div.chat-info').length).toBe(2);
expect($chat_content.find('div.chat-info:last').html()).toBe("newguy has entered the room");
expect($chat_content.find('div.chat-info:last').html()).toBe("newguy has entered the groupchat");
// Add another entrant, otherwise the above message will be
// collapsed if "newguy" leaves immediately again
@ -483,7 +483,7 @@
});
_converse.connection._dataRecv(test_utils.createRequest(presence));
expect($chat_content[0].querySelectorAll('div.chat-info').length).toBe(3);
expect($chat_content.find('div.chat-info:last').html()).toBe("newgirl has entered the room");
expect($chat_content.find('div.chat-info:last').html()).toBe("newgirl has entered the groupchat");
// Don't show duplicate join messages
presence = $pres({
@ -525,7 +525,7 @@
_converse.connection._dataRecv(test_utils.createRequest(presence));
expect($chat_content.find('div.chat-info').length).toBe(4);
expect($chat_content.find('div.chat-info:last').html()).toBe(
'newguy has left the room. '+
'newguy has left the groupchat. '+
'"Disconnected: Replaced by new connection"');
// When the user immediately joins again, we collapse the
@ -542,7 +542,7 @@
_converse.connection._dataRecv(test_utils.createRequest(presence));
expect($chat_content.find('div.chat-info').length).toBe(4);
var $msg_el = $chat_content.find('div.chat-info:last');
expect($msg_el.html()).toBe("newguy has left and re-entered the room");
expect($msg_el.html()).toBe("newguy has left and re-entered the groupchat");
expect($msg_el.data('leavejoin')).toBe('"newguy"');
presence = $pres({
@ -559,7 +559,7 @@
_converse.connection._dataRecv(test_utils.createRequest(presence));
expect($chat_content.find('div.chat-info').length).toBe(4);
$msg_el = $chat_content.find('div.chat-info:last');
expect($msg_el.html()).toBe('newguy has left the room');
expect($msg_el.html()).toBe('newguy has left the groupchat');
expect($msg_el.data('leave')).toBe('"newguy"');
presence = $pres({
@ -574,7 +574,7 @@
});
_converse.connection._dataRecv(test_utils.createRequest(presence));
expect($chat_content[0].querySelectorAll('div.chat-info').length).toBe(5);
expect($chat_content.find('div.chat-info:last').html()).toBe("nomorenicks has entered the room");
expect($chat_content.find('div.chat-info:last').html()).toBe("nomorenicks has entered the groupchat");
presence = $pres({
to: 'dummy@localhost/_converse.js-290918392',
@ -588,7 +588,7 @@
});
_converse.connection._dataRecv(test_utils.createRequest(presence));
expect($chat_content[0].querySelectorAll('div.chat-info').length).toBe(5);
expect($chat_content.find('div.chat-info:last').html()).toBe("nomorenicks has entered and left the room");
expect($chat_content.find('div.chat-info:last').html()).toBe("nomorenicks has entered and left the groupchat");
presence = $pres({
to: 'dummy@localhost/_converse.js-29092160',
@ -602,7 +602,7 @@
});
_converse.connection._dataRecv(test_utils.createRequest(presence));
expect($chat_content[0].querySelectorAll('div.chat-info').length).toBe(5);
expect($chat_content.find('div.chat-info:last').html()).toBe("nomorenicks has entered the room");
expect($chat_content.find('div.chat-info:last').html()).toBe("nomorenicks has entered the groupchat");
done();
}));
@ -622,7 +622,7 @@
expect(indicator.querySelector('time').textContent).toEqual(moment().startOf('day').format("dddd MMM Do YYYY"));
expect(chat_content.querySelectorAll('div.chat-info').length).toBe(1);
expect(chat_content.querySelector('div.chat-info').textContent).toBe(
"dummy has entered the room"
"dummy has entered the groupchat"
);
var baseTime = new Date();
@ -659,7 +659,7 @@
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"
"some1 has entered the groupchat"
);
jasmine.clock().tick(ONE_DAY_LATER);
@ -688,7 +688,7 @@
expect(indicator.querySelector('time').textContent).toEqual(moment().startOf('day').format("dddd MMM Do YYYY"));
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. '+
'some1 has left the groupchat. '+
'"Disconnected: Replaced by new connection"');
jasmine.clock().tick(ONE_DAY_LATER);
@ -722,7 +722,7 @@
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.querySelectorAll('div.chat-info').length).toBe(4);
expect($chat_content.find('div.chat-info:last').html()).toBe("newguy has entered the room");
expect($chat_content.find('div.chat-info:last').html()).toBe("newguy has entered the groupchat");
jasmine.clock().tick(ONE_DAY_LATER);
@ -763,7 +763,7 @@
expect($indicator.find('time').text()).toEqual(moment().startOf('day').format("dddd MMM Do YYYY"));
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. '+
'newguy has left the groupchat. '+
'"Disconnected: Replaced by new connection"');
jasmine.clock().uninstall();
@ -811,7 +811,7 @@
.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('field', {'type':'text-single', 'var':'muc#roominfo_occupants', 'label':'Number of participants'})
.c('value').t(0);
_converse.connection._dataRecv(test_utils.createRequest(features_stanza));
test_utils.waitUntil(() => _.get(view.el.querySelector('.chatroom-description'), 'textContent'))
@ -867,8 +867,8 @@
'type': 'groupchat'
}).c('body').t(message).tree();
view.model.onMessage(msg);
expect(_.includes($(view.el).find('.chat-msg-author').text(), '**Dyon van de Wege')).toBeTruthy();
expect($(view.el).find('.chat-msg-text').text()).toBe(' is tired');
expect(_.includes($(view.el).find('.chat-msg__author').text(), '**Dyon van de Wege')).toBeTruthy();
expect($(view.el).find('.chat-msg__text').text()).toBe(' is tired');
message = '/me is as well';
msg = $msg({
@ -878,8 +878,8 @@
type: 'groupchat'
}).c('body').t(message).tree();
view.model.onMessage(msg);
expect(_.includes($(view.el).find('.chat-msg-author:last').text(), '**Max Mustermann')).toBeTruthy();
expect($(view.el).find('.chat-msg-text:last').text()).toBe(' is as well');
expect(_.includes($(view.el).find('.chat-msg__author:last').text(), '**Max Mustermann')).toBeTruthy();
expect($(view.el).find('.chat-msg__text:last').text()).toBe(' is as well');
done();
});
}));
@ -1065,7 +1065,7 @@
'var': 'muc#roomconfig_passwordprotectedroom'})
.c('value').t(1).up().up()
.c('field', {'type': 'fixed'})
.c('value').t('If a password is required to enter this room,'+
.c('value').t('If a password is required to enter this groupchat,'+
'you must specify the password below.').up().up()
.c('field', {
'label': 'Password',
@ -1113,7 +1113,7 @@
}).catch(_.partial(_converse.log, _, Strophe.LogLevel.FATAL));
}));
it("shows all members even if they're not currently present in the room",
it("shows all members even if they're not currently present in the groupchat",
mock.initConverseWithPromises(
null, ['rosterGroupsFetched'], {},
function (done, _converse) {
@ -1145,7 +1145,7 @@
expect(occupants.querySelectorAll('li .occupant-nick')[index].textContent.trim()).toBe(mock.chatroom_names[i]);
}
// Test users leaving the room
// Test users leaving the groupchat
// http://xmpp.org/extensions/xep-0045.html#exit
for (i=mock.chatroom_names.length-1; i>-1; i--) {
name = mock.chatroom_names[i];
@ -1168,7 +1168,7 @@
}).catch(_.partial(console.error, _));
}));
it("shows users currently present in the room",
it("shows users currently present in the groupchat",
mock.initConverseWithPromises(
null, ['rosterGroupsFetched'], {},
function (done, _converse) {
@ -1200,7 +1200,7 @@
expect(occupants.querySelectorAll('li .occupant-nick')[index].textContent.trim()).toBe(mock.chatroom_names[i]);
}
// Test users leaving the room
// Test users leaving the groupchat
// http://xmpp.org/extensions/xep-0045.html#exit
for (i=mock.chatroom_names.length-1; i>-1; i--) {
name = mock.chatroom_names[i];
@ -1310,7 +1310,7 @@
expect($(occupants).last().find('.badge').length).toBe(1);
expect($(occupants).last().find('.badge').last().text()).toBe('Visitor');
expect($(occupants).last().attr('title')).toBe(
contact_jid + ' This user can NOT send messages in this room. Click to mention visitorwoman in your message.'
contact_jid + ' This user can NOT send messages in this groupchat. Click to mention visitorwoman in your message.'
);
done();
}).catch(_.partial(console.error, _));
@ -1395,7 +1395,7 @@
expect(view.join).toHaveBeenCalled();
// The user has just entered the room (because join was called)
// The user has just entered the groupchat (because join was called)
// and receives their own presence from the server.
// See example 24:
// http://xmpp.org/extensions/xep-0045.html#enter-pres
@ -1437,7 +1437,7 @@
var view = _converse.chatboxviews.get('lounge@localhost');
// XXX: cheating a lttle bit, normally this'll be set after
// receiving the features for the room.
// receiving the features for the groupchat.
view.model.set('open', 'true');
spyOn(view.model, 'directInvite').and.callThrough();
@ -1545,7 +1545,7 @@
view.model.onMessage(message.nodeTree);
var $chat_content = $(view.el).find('.chat-content');
expect($chat_content.find('.chat-msg').length).toBe(1);
expect($chat_content.find('.chat-msg-text').text()).toBe(text);
expect($chat_content.find('.chat-msg__text').text()).toBe(text);
expect(_converse.emit).toHaveBeenCalledWith('message', jasmine.any(Object));
done();
});
@ -1583,7 +1583,7 @@
}).c('body').t(text);
view.model.onMessage(message.nodeTree);
expect($chat_content.find('.chat-msg').length).toBe(1);
expect($chat_content.find('.chat-msg-text').last().text()).toBe(text);
expect($chat_content.find('.chat-msg__text').last().text()).toBe(text);
// We don't emit an event if it's our own message
expect(_converse.emit.calls.count(), 1);
done();
@ -1624,7 +1624,7 @@
// Now check that the message appears inside the chatbox in the DOM
var $chat_content = $(view.el).find('.chat-content');
var msg_txt = $chat_content.find('.chat-msg:last').find('.chat-msg-text').text();
var msg_txt = $chat_content.find('.chat-msg:last').find('.chat-msg__text').text();
expect(msg_txt).toEqual(message);
expect(view.content.scrollTop).toBe(0);
done();
@ -1632,7 +1632,7 @@
});
}));
it("shows received chatroom subject messages",
it("shows received groupchat subject messages",
mock.initConverseWithPromises(
null, ['rosterGroupsFetched'], {},
function (done, _converse) {
@ -1724,7 +1724,7 @@
expect($occupants.children().first(0).find('.occupant-nick').text().trim()).toBe("oldnick");
expect($chat_content.find('div.chat-info').length).toBe(1);
expect($chat_content.find('div.chat-info:first').html()).toBe("oldnick has entered the room");
expect($chat_content.find('div.chat-info:first').html()).toBe("oldnick has entered the groupchat");
var presence = $pres().attrs({
from:'lounge@localhost/oldnick',
@ -1765,7 +1765,7 @@
.c('status').attrs({code:'110'}).nodeTree;
_converse.connection._dataRecv(test_utils.createRequest(presence));
// XXX: currently we still have an additional "has entered the room"
// XXX: currently we still have an additional "has entered the groupchat"
// notification for the new nickname. Ideally we'd not have
// that, but that's probably not possible without some
// significant refactoring.
@ -1780,7 +1780,7 @@
});
}));
it("queries for the room information before attempting to join the user",
it("queries for the groupchat information before attempting to join the user",
mock.initConverseWithPromises(
null, ['rosterGroupsFetched'], {},
function (done, _converse) {
@ -1794,7 +1794,7 @@
_converse.api.rooms.open('coven@chat.shakespeare.lit', {'nick': 'some1'});
// Check that the room queried for the feautures.
// Check that the groupchat queried for the feautures.
expect(sent_IQ.toLocaleString()).toBe(
"<iq from='dummy@localhost/resource' to='coven@chat.shakespeare.lit' type='get' xmlns='jabber:client' id='"+IQ_id+"'>"+
"<query xmlns='http://jabber.org/protocol/disco#info'/>"+
@ -1854,7 +1854,7 @@
});
}));
it("updates the shown features when the room configuration has changed",
it("updates the shown features when the groupchat configuration has changed",
mock.initConverseWithPromises(
null, ['rosterGroupsFetched'], {},
function (done, _converse) {
@ -1954,7 +1954,7 @@
.c('status', {code: '172'});
_converse.connection._dataRecv(test_utils.createRequest(message));
var $chat_body = $(view.el.querySelector('.chatroom-body'));
expect($chat_body.find('.message:last').text()).toBe('This room is now no longer anonymous');
expect($chat_body.find('.message:last').text()).toBe('This groupchat is now no longer anonymous');
done();
});
}));
@ -1997,15 +1997,17 @@
.c('status').attrs({code:'307'}).nodeTree;
_converse.connection._dataRecv(test_utils.createRequest(presence));
var view = _converse.chatboxviews.get('lounge@localhost');
const view = _converse.chatboxviews.get('lounge@localhost');
expect($(view.el.querySelector('.chat-area')).is(':visible')).toBeFalsy();
expect($(view.el.querySelector('.occupants')).is(':visible')).toBeFalsy();
var $chat_body = $(view.el.querySelector('.chatroom-body'));
expect($chat_body.find('.disconnect-msg').text()).toBe(
'You have been kicked from this room'+
'This action was done by Fluellen.'+
'The reason given is: "Avaunt, you cullion!".'
);
const chat_body = view.el.querySelector('.chatroom-body');
expect(chat_body.querySelectorAll('.disconnect-msg').length).toBe(3);
expect(chat_body.querySelector('.disconnect-msg:first-child').textContent).toBe(
'You have been kicked from this groupchat');
expect(chat_body.querySelector('.disconnect-msg:nth-child(2)').textContent).toBe(
'This action was done by Fluellen.');
expect(chat_body.querySelector('.disconnect-msg:nth-child(3)').textContent).toBe(
'The reason given is: "Avaunt, you cullion!".');
done();
});
}));
@ -2091,7 +2093,7 @@
});
describe("Each chat room can take special commands", function () {
describe("Each chat groupchat can take special commands", function () {
it("/help to show the available commands",
mock.initConverseWithPromises(
@ -2102,7 +2104,7 @@
var view = _converse.chatboxviews.get('lounge@localhost');
spyOn(view, 'onMessageSubmitted').and.callThrough();
var textarea = view.el.querySelector('.chat-textarea');
textarea.value = '/help This is the room subject';
textarea.value = '/help This is the groupchat subject';
view.keyPressed({
target: textarea,
preventDefault: _.noop,
@ -2113,26 +2115,26 @@
const info_messages = Array.prototype.slice.call(view.el.querySelectorAll('.chat-info'), 0);
expect(info_messages.length).toBe(17);
expect(info_messages.pop().textContent).toBe('/voice: Allow muted user to post messages');
expect(info_messages.pop().textContent).toBe('/topic: Set room subject (alias for /subject)');
expect(info_messages.pop().textContent).toBe('/subject: Set room subject');
expect(info_messages.pop().textContent).toBe('/topic: Set groupchat subject (alias for /subject)');
expect(info_messages.pop().textContent).toBe('/subject: Set groupchat subject');
expect(info_messages.pop().textContent).toBe('/revoke: Revoke user\'s membership');
expect(info_messages.pop().textContent).toBe('/owner: Grant ownership of this room');
expect(info_messages.pop().textContent).toBe('/owner: Grant ownership of this groupchat');
expect(info_messages.pop().textContent).toBe('/op: Grant moderator role to user');
expect(info_messages.pop().textContent).toBe('/nick: Change your nickname');
expect(info_messages.pop().textContent).toBe('/mute: Remove user\'s ability to post messages');
expect(info_messages.pop().textContent).toBe('/member: Grant membership to a user');
expect(info_messages.pop().textContent).toBe('/me: Write in 3rd person');
expect(info_messages.pop().textContent).toBe('/kick: Kick user from room');
expect(info_messages.pop().textContent).toBe('/kick: Kick user from groupchat');
expect(info_messages.pop().textContent).toBe('/help: Show this menu');
expect(info_messages.pop().textContent).toBe('/deop: Change user role to participant');
expect(info_messages.pop().textContent).toBe('/clear: Remove messages');
expect(info_messages.pop().textContent).toBe('/ban: Ban user from room');
expect(info_messages.pop().textContent).toBe('/ban: Ban user from groupchat');
expect(info_messages.pop().textContent).toBe('/admin: Change user\'s affiliation to admin');
done();
});
}));
it("/topic to set the room topic",
it("/topic to set the groupchat topic",
mock.initConverseWithPromises(
null, ['rosterGroupsFetched'], {},
function (done, _converse) {
@ -2147,7 +2149,7 @@
});
// Check the alias /topic
var textarea = view.el.querySelector('.chat-textarea');
textarea.value = '/topic This is the room subject';
textarea.value = '/topic This is the groupchat subject';
view.keyPressed({
target: textarea,
preventDefault: _.noop,
@ -2155,7 +2157,7 @@
});
expect(view.onMessageSubmitted).toHaveBeenCalled();
expect(_converse.connection.send).toHaveBeenCalled();
expect(sent_stanza.textContent).toBe('This is the room subject');
expect(sent_stanza.textContent).toBe('This is the groupchat subject');
// Check /subject
textarea.value = '/subject This is a new subject';
@ -2413,7 +2415,7 @@
spyOn(view, 'showChatEvent').and.callThrough();
spyOn(view, 'validateRoleChangeCommand').and.callThrough();
// New user enters the room
// New user enters the groupchat
/* <presence
* from='coven@chat.shakespeare.lit/thirdwitch'
* id='27C55F89-1C6A-459A-9EB5-77690145D624'
@ -2436,7 +2438,7 @@
});
_converse.connection._dataRecv(test_utils.createRequest(presence));
var info_msgs = Array.prototype.slice.call(view.el.querySelectorAll('.chat-info'), 0);
expect(info_msgs.pop().textContent).toBe("trustworthyguy has entered the room");
expect(info_msgs.pop().textContent).toBe("trustworthyguy has entered the groupchat");
var textarea = view.el.querySelector('.chat-textarea')
textarea.value = '/op';
@ -2553,7 +2555,7 @@
spyOn(view, 'showChatEvent').and.callThrough();
spyOn(view, 'validateRoleChangeCommand').and.callThrough();
// New user enters the room
// New user enters the groupchat
/* <presence
* from='coven@chat.shakespeare.lit/thirdwitch'
* id='27C55F89-1C6A-459A-9EB5-77690145D624'
@ -2576,7 +2578,7 @@
});
_converse.connection._dataRecv(test_utils.createRequest(presence));
var info_msgs = Array.prototype.slice.call(view.el.querySelectorAll('.chat-info'), 0);
expect(info_msgs.pop().textContent).toBe("annoyingGuy has entered the room");
expect(info_msgs.pop().textContent).toBe("annoyingGuy has entered the groupchat");
var textarea = view.el.querySelector('.chat-textarea')
textarea.value = '/mute';
@ -2675,9 +2677,9 @@
}));
});
describe("When attempting to enter a chatroom", function () {
describe("When attempting to enter a groupchat", function () {
it("will show an error message if the room requires a password",
it("will show an error message if the groupchat requires a password",
mock.initConverseWithPromises(
null, ['rosterGroupsFetched', 'chatBoxesFetched'], {},
function (done, _converse) {
@ -2701,7 +2703,7 @@
var $chat_body = $(view.el).find('.chatroom-body');
expect(view.renderPasswordForm).toHaveBeenCalled();
expect($chat_body.find('form.chatroom-form').length).toBe(1);
expect($chat_body.find('legend').text()).toBe('This chatroom requires a password');
expect($chat_body.find('legend').text()).toBe('This groupchat requires a password');
// Let's submit the form
spyOn(view, 'join');
@ -2713,7 +2715,7 @@
}).catch(_.partial(console.error, _));
}));
it("will show an error message if the room is members-only and the user not included",
it("will show an error message if the groupchat is members-only and the user not included",
mock.initConverseWithPromises(
null, ['rosterGroupsFetched'], {},
function (done, _converse) {
@ -2731,7 +2733,8 @@
var view = _converse.chatboxviews.get('problematic@muc.localhost');
spyOn(view, 'showErrorMessage').and.callThrough();
_converse.connection._dataRecv(test_utils.createRequest(presence));
expect($(view.el).find('.chatroom-body p:last').text()).toBe('You are not on the member list of this room.');
expect(view.el.querySelector('.chatroom-body .disconnect-container .disconnect-msg:last-child').textContent)
.toBe('You are not on the member list of this groupchat.');
done();
}).catch(_.partial(console.error, _));
}));
@ -2754,7 +2757,8 @@
var view = _converse.chatboxviews.get('problematic@muc.localhost');
spyOn(view, 'showErrorMessage').and.callThrough();
_converse.connection._dataRecv(test_utils.createRequest(presence));
expect($(view.el).find('.chatroom-body p:last').text()).toBe('You have been banned from this room.');
expect(view.el.querySelector('.chatroom-body .disconnect-container .disconnect-msg:last-child').textContent)
.toBe('You have been banned from this groupchat.');
done();
}).catch(_.partial(console.error, _));
}));
@ -2821,7 +2825,7 @@
spyOn(view, 'showErrorMessage').and.callThrough();
spyOn(view, 'join').and.callThrough();
// Simulate repeatedly that there's already someone in the room
// Simulate repeatedly that there's already someone in the groupchat
// with that nickname
_converse.connection._dataRecv(test_utils.createRequest(presence));
expect(view.join).toHaveBeenCalledWith('dummy-2');
@ -2848,7 +2852,7 @@
}).catch(_.partial(console.error, _));
}));
it("will show an error message if the user is not allowed to have created the room",
it("will show an error message if the user is not allowed to have created the groupchat",
mock.initConverseWithPromises(
null, ['rosterGroupsFetched'], {},
function (done, _converse) {
@ -2866,7 +2870,8 @@
var view = _converse.chatboxviews.get('problematic@muc.localhost');
spyOn(view, 'showErrorMessage').and.callThrough();
_converse.connection._dataRecv(test_utils.createRequest(presence));
expect($(view.el).find('.chatroom-body p:last').text()).toBe('You are not allowed to create new rooms.');
expect(view.el.querySelector('.chatroom-body .disconnect-container .disconnect-msg:last-child').textContent)
.toBe('You are not allowed to create new rooms.');
done();
}).catch(_.partial(console.error, _));
}));
@ -2889,12 +2894,13 @@
var view = _converse.chatboxviews.get('problematic@muc.localhost');
spyOn(view, 'showErrorMessage').and.callThrough();
_converse.connection._dataRecv(test_utils.createRequest(presence));
expect($(view.el).find('.chatroom-body p:last').text()).toBe("Your nickname doesn't conform to this room's policies.");
expect(view.el.querySelector('.chatroom-body .disconnect-container .disconnect-msg:last-child').textContent)
.toBe("Your nickname doesn't conform to this groupchat's policies.");
done();
}).catch(_.partial(console.error, _));
}));
it("will show an error message if the room doesn't yet exist",
it("will show an error message if the groupchat doesn't yet exist",
mock.initConverseWithPromises(
null, ['rosterGroupsFetched'], {},
function (done, _converse) {
@ -2912,12 +2918,13 @@
var view = _converse.chatboxviews.get('problematic@muc.localhost');
spyOn(view, 'showErrorMessage').and.callThrough();
_converse.connection._dataRecv(test_utils.createRequest(presence));
expect($(view.el).find('.chatroom-body p:last').text()).toBe("This room does not (yet) exist.");
expect(view.el.querySelector('.chatroom-body .disconnect-container .disconnect-msg:last-child').textContent)
.toBe("This groupchat does not (yet) exist.");
done();
}).catch(_.partial(console.error, _));
}));
it("will show an error message if the room has reached its maximum number of occupants",
it("will show an error message if the groupchat has reached its maximum number of participants",
mock.initConverseWithPromises(
null, ['rosterGroupsFetched'], {},
function (done, _converse) {
@ -2935,7 +2942,8 @@
var view = _converse.chatboxviews.get('problematic@muc.localhost');
spyOn(view, 'showErrorMessage').and.callThrough();
_converse.connection._dataRecv(test_utils.createRequest(presence));
expect($(view.el).find('.chatroom-body p:last').text()).toBe("This room has reached its maximum number of occupants.");
expect(view.el.querySelector('.chatroom-body .disconnect-container .disconnect-msg:last-child').textContent)
.toBe("This groupchat has reached its maximum number of participants.");
done();
}).catch(_.partial(console.error, _));
}));
@ -3173,9 +3181,8 @@
roomspanel.el.querySelector('.show-add-muc-modal').click();
test_utils.closeControlBox(_converse);
const modal = roomspanel.add_room_modal;
test_utils.waitUntil(function () {
return u.isVisible(modal.el);
}, 1000).then(function () {
test_utils.waitUntil(() => u.isVisible(modal.el), 1000)
.then(function () {
spyOn(_converse.ChatRoom.prototype, 'getRoomFeatures').and.callFake(function () {
var deferred = new $.Deferred();
deferred.resolve();
@ -3263,7 +3270,7 @@
mock.initConverseWithPromises(
null, ['rosterGroupsFetched'], {'allow_bookmarks': false},
function (done, _converse) {
// XXX: we set `allow_bookmarks` to false, so that the rooms
// XXX: we set `allow_bookmarks` to false, so that the groupchats
// list gets rendered. Otherwise we would have to mock
// the bookmark stanza exchange.
@ -3351,7 +3358,7 @@
.c('status', {code: '110'});
_converse.connection._dataRecv(test_utils.createRequest(presence));
expect($chat_content[0].querySelectorAll('div.chat-info').length).toBe(2);
expect($chat_content.find('div.chat-info:first').html()).toBe("some1 has entered the room");
expect($chat_content.find('div.chat-info:first').html()).toBe("some1 has entered the groupchat");
expect($chat_content.find('div.chat-info:last').html()).toBe("some1 is now a moderator");
presence = $pres({
@ -3366,7 +3373,7 @@
});
_converse.connection._dataRecv(test_utils.createRequest(presence));
expect($chat_content[0].querySelectorAll('div.chat-info').length).toBe(3);
expect($chat_content.find('div.chat-info:last').html()).toBe("newguy has entered the room");
expect($chat_content.find('div.chat-info:last').html()).toBe("newguy has entered the groupchat");
presence = $pres({
to: 'dummy@localhost/_converse.js-29092160',
@ -3380,7 +3387,7 @@
});
_converse.connection._dataRecv(test_utils.createRequest(presence));
expect($chat_content[0].querySelectorAll('div.chat-info').length).toBe(4);
expect($chat_content.find('div.chat-info:last').html()).toBe("nomorenicks has entered the room");
expect($chat_content.find('div.chat-info:last').html()).toBe("nomorenicks has entered the groupchat");
// See XEP-0085 http://xmpp.org/extensions/xep-0085.html#definitions
@ -3397,10 +3404,10 @@
// Check that the notification appears inside the chatbox in the DOM
var events = view.el.querySelectorAll('.chat-event');
expect(events.length).toBe(4);
expect(events[0].textContent).toEqual('some1 has entered the room');
expect(events[0].textContent).toEqual('some1 has entered the groupchat');
expect(events[1].textContent).toEqual('some1 is now a moderator');
expect(events[2].textContent).toEqual('newguy has entered the room');
expect(events[3].textContent).toEqual('nomorenicks has entered the room');
expect(events[2].textContent).toEqual('newguy has entered the groupchat');
expect(events[3].textContent).toEqual('nomorenicks has entered the groupchat');
var notifications = view.el.querySelectorAll('.chat-state-notification');
expect(notifications.length).toBe(1);
@ -3422,10 +3429,10 @@
events = view.el.querySelectorAll('.chat-event');
expect(events.length).toBe(4);
expect(events[0].textContent).toEqual('some1 has entered the room');
expect(events[0].textContent).toEqual('some1 has entered the groupchat');
expect(events[1].textContent).toEqual('some1 is now a moderator');
expect(events[2].textContent).toEqual('newguy has entered the room');
expect(events[3].textContent).toEqual('nomorenicks has entered the room');
expect(events[2].textContent).toEqual('newguy has entered the groupchat');
expect(events[3].textContent).toEqual('nomorenicks has entered the groupchat');
notifications = view.el.querySelectorAll('.chat-state-notification');
expect(notifications.length).toBe(1);
@ -3443,10 +3450,10 @@
view.model.onMessage(msg);
events = view.el.querySelectorAll('.chat-event');
expect(events.length).toBe(4);
expect(events[0].textContent).toEqual('some1 has entered the room');
expect(events[0].textContent).toEqual('some1 has entered the groupchat');
expect(events[1].textContent).toEqual('some1 is now a moderator');
expect(events[2].textContent).toEqual('newguy has entered the room');
expect(events[3].textContent).toEqual('nomorenicks has entered the room');
expect(events[2].textContent).toEqual('newguy has entered the groupchat');
expect(events[3].textContent).toEqual('nomorenicks has entered the groupchat');
notifications = view.el.querySelectorAll('.chat-state-notification');
expect(notifications.length).toBe(2);
@ -3467,17 +3474,17 @@
var messages = view.el.querySelectorAll('.message');
expect(messages.length).toBe(8);
expect(view.el.querySelectorAll('.chat-msg').length).toBe(1);
expect(view.el.querySelector('.chat-msg .chat-msg-text').textContent).toBe('hello world');
expect(view.el.querySelector('.chat-msg .chat-msg__text').textContent).toBe('hello world');
// Test that the composing notifications get removed
// via timeout.
timeout_functions[0]();
events = view.el.querySelectorAll('.chat-event');
expect(events.length).toBe(4);
expect(events[0].textContent).toEqual('some1 has entered the room');
expect(events[0].textContent).toEqual('some1 has entered the groupchat');
expect(events[1].textContent).toEqual('some1 is now a moderator');
expect(events[2].textContent).toEqual('newguy has entered the room');
expect(events[3].textContent).toEqual('nomorenicks has entered the room');
expect(events[2].textContent).toEqual('newguy has entered the groupchat');
expect(events[3].textContent).toEqual('nomorenicks has entered the groupchat');
notifications = view.el.querySelectorAll('.chat-state-notification');
expect(notifications.length).toBe(1);
@ -3486,10 +3493,10 @@
timeout_functions[1]();
events = view.el.querySelectorAll('.chat-event');
expect(events.length).toBe(4);
expect(events[0].textContent).toEqual('some1 has entered the room');
expect(events[0].textContent).toEqual('some1 has entered the groupchat');
expect(events[1].textContent).toEqual('some1 is now a moderator');
expect(events[2].textContent).toEqual('newguy has entered the room');
expect(events[3].textContent).toEqual('nomorenicks has entered the room');
expect(events[2].textContent).toEqual('newguy has entered the groupchat');
expect(events[3].textContent).toEqual('nomorenicks has entered the groupchat');
notifications = view.el.querySelectorAll('.chat-state-notification');
expect(notifications.length).toBe(0);
@ -3528,7 +3535,7 @@
}).up()
.c('status', {code: '110'});
_converse.connection._dataRecv(test_utils.createRequest(presence));
expect($chat_content.find('div.chat-info:first').html()).toBe("some1 has entered the room");
expect($chat_content.find('div.chat-info:first').html()).toBe("some1 has entered the groupchat");
presence = $pres({
to: 'dummy@localhost/_converse.js-29092160',
@ -3542,7 +3549,7 @@
});
_converse.connection._dataRecv(test_utils.createRequest(presence));
expect($chat_content[0].querySelectorAll('div.chat-info').length).toBe(2);
expect($chat_content.find('div.chat-info:last').html()).toBe("newguy has entered the room");
expect($chat_content.find('div.chat-info:last').html()).toBe("newguy has entered the groupchat");
presence = $pres({
to: 'dummy@localhost/_converse.js-29092160',
@ -3556,7 +3563,7 @@
});
_converse.connection._dataRecv(test_utils.createRequest(presence));
expect($chat_content[0].querySelectorAll('div.chat-info').length).toBe(3);
expect($chat_content.find('div.chat-info:last').html()).toBe("nomorenicks has entered the room");
expect($chat_content.find('div.chat-info:last').html()).toBe("nomorenicks has entered the groupchat");
// See XEP-0085 http://xmpp.org/extensions/xep-0085.html#definitions
@ -3572,9 +3579,9 @@
// Check that the notification appears inside the chatbox in the DOM
var events = view.el.querySelectorAll('.chat-event');
expect(events.length).toBe(3);
expect(events[0].textContent).toEqual('some1 has entered the room');
expect(events[1].textContent).toEqual('newguy has entered the room');
expect(events[2].textContent).toEqual('nomorenicks has entered the room');
expect(events[0].textContent).toEqual('some1 has entered the groupchat');
expect(events[1].textContent).toEqual('newguy has entered the groupchat');
expect(events[2].textContent).toEqual('nomorenicks has entered the groupchat');
var notifications = view.el.querySelectorAll('.chat-state-notification');
expect(notifications.length).toBe(1);
@ -3591,9 +3598,9 @@
events = view.el.querySelectorAll('.chat-event');
expect(events.length).toBe(3);
expect(events[0].textContent).toEqual('some1 has entered the room');
expect(events[1].textContent).toEqual('newguy has entered the room');
expect(events[2].textContent).toEqual('nomorenicks has entered the room');
expect(events[0].textContent).toEqual('some1 has entered the groupchat');
expect(events[1].textContent).toEqual('newguy has entered the groupchat');
expect(events[2].textContent).toEqual('nomorenicks has entered the groupchat');
notifications = view.el.querySelectorAll('.chat-state-notification');
expect(notifications.length).toBe(1);
@ -3609,9 +3616,9 @@
view.model.onMessage(msg);
events = view.el.querySelectorAll('.chat-event');
expect(events.length).toBe(3);
expect(events[0].textContent).toEqual('some1 has entered the room');
expect(events[1].textContent).toEqual('newguy has entered the room');
expect(events[2].textContent).toEqual('nomorenicks has entered the room');
expect(events[0].textContent).toEqual('some1 has entered the groupchat');
expect(events[1].textContent).toEqual('newguy has entered the groupchat');
expect(events[2].textContent).toEqual('nomorenicks has entered the groupchat');
notifications = view.el.querySelectorAll('.chat-state-notification');
expect(notifications.length).toBe(2);
@ -3628,9 +3635,9 @@
view.model.onMessage(msg);
events = view.el.querySelectorAll('.chat-event');
expect(events.length).toBe(3);
expect(events[0].textContent).toEqual('some1 has entered the room');
expect(events[1].textContent).toEqual('newguy has entered the room');
expect(events[2].textContent).toEqual('nomorenicks has entered the room');
expect(events[0].textContent).toEqual('some1 has entered the groupchat');
expect(events[1].textContent).toEqual('newguy has entered the groupchat');
expect(events[2].textContent).toEqual('nomorenicks has entered the groupchat');
notifications = view.el.querySelectorAll('.chat-state-notification');
expect(notifications.length).toBe(2);

View File

@ -364,7 +364,7 @@
}, 1000);
}).then(function () {
// Check that the image renders
expect(view.el.querySelector('.chat-msg .chat-msg-media').innerHTML.trim()).toEqual(
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">'+
@ -472,7 +472,7 @@
}, 1000);
}).then(function () {
// Check that the image renders
expect(view.el.querySelector('.chat-msg .chat-msg-media').innerHTML.trim()).toEqual(
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>')
@ -683,7 +683,7 @@
expect(view.el.querySelector('.chat-content progress').getAttribute('value')).toBe('0.5');
message.set('progress', 1);
expect(view.el.querySelector('.chat-content progress').getAttribute('value')).toBe('1');
expect(view.el.querySelector('.chat-content .chat-msg-text').textContent).toBe('Uploading file: my-juliet.jpg, 22.91 KB');
expect(view.el.querySelector('.chat-content .chat-msg__text').textContent).toBe('Uploading file: my-juliet.jpg, 22.91 KB');
done();
});
var sent_stanza;

File diff suppressed because it is too large Load Diff

View File

@ -47,7 +47,7 @@
"<presence xmlns='jabber:client'>"+
"<status>Hello world</status>"+
"<priority>0</priority>"+
"<c xmlns='http://jabber.org/protocol/caps' hash='sha-1' node='https://conversejs.org' ver='3RLZIW6Gu1JDLZXCD6HFV9MiDQ4='/>"+
"<c xmlns='http://jabber.org/protocol/caps' hash='sha-1' node='https://conversejs.org' ver='nE765l4CRVrSUEIPAdtgCw4+5cc='/>"+
"</presence>"
);
_converse.priority = 2;
@ -57,7 +57,7 @@
"<show>away</show>"+
"<status>Going jogging</status>"+
"<priority>2</priority>"+
"<c xmlns='http://jabber.org/protocol/caps' hash='sha-1' node='https://conversejs.org' ver='3RLZIW6Gu1JDLZXCD6HFV9MiDQ4='/>"+
"<c xmlns='http://jabber.org/protocol/caps' hash='sha-1' node='https://conversejs.org' ver='nE765l4CRVrSUEIPAdtgCw4+5cc='/>"+
"</presence>"
);
@ -68,7 +68,7 @@
"<show>dnd</show>"+
"<status>Doing taxes</status>"+
"<priority>0</priority>"+
"<c xmlns='http://jabber.org/protocol/caps' hash='sha-1' node='https://conversejs.org' ver='3RLZIW6Gu1JDLZXCD6HFV9MiDQ4='/>"+
"<c xmlns='http://jabber.org/protocol/caps' hash='sha-1' node='https://conversejs.org' ver='nE765l4CRVrSUEIPAdtgCw4+5cc='/>"+
"</presence>"
);
}));
@ -97,7 +97,7 @@
.toBe("<presence xmlns='jabber:client'>"+
"<status>My custom status</status>"+
"<priority>0</priority>"+
"<c xmlns='http://jabber.org/protocol/caps' hash='sha-1' node='https://conversejs.org' ver='3RLZIW6Gu1JDLZXCD6HFV9MiDQ4='/>"+
"<c xmlns='http://jabber.org/protocol/caps' hash='sha-1' node='https://conversejs.org' ver='nE765l4CRVrSUEIPAdtgCw4+5cc='/>"+
"</presence>")
return test_utils.waitUntil(function () {
@ -113,7 +113,7 @@
modal.el.querySelector('[type="submit"]').click();
expect(_converse.connection.send.calls.mostRecent().args[0].toLocaleString())
.toBe("<presence xmlns='jabber:client'><show>dnd</show><status>My custom status</status><priority>0</priority>"+
"<c xmlns='http://jabber.org/protocol/caps' hash='sha-1' node='https://conversejs.org' ver='3RLZIW6Gu1JDLZXCD6HFV9MiDQ4='/>"+
"<c xmlns='http://jabber.org/protocol/caps' hash='sha-1' node='https://conversejs.org' ver='nE765l4CRVrSUEIPAdtgCw4+5cc='/>"+
"</presence>")
done();
});

View File

@ -54,7 +54,38 @@
));
});
describe("A room shown in the rooms list", function () {
describe("A groupchat shown in the groupchats list", function () {
it("is highlighted if its currently open", mock.initConverseWithPromises(
null, ['rosterGroupsFetched'],
{ whitelisted_plugins: ['converse-roomslist'],
allow_bookmarks: false // Makes testing easier, otherwise we
// have to mock stanza traffic.
}, function (done, _converse) {
spyOn(_converse, 'isSingleton').and.callFake(function () {
return true;
});
test_utils.openControlBox();
_converse.api.rooms.open('coven@chat.shakespeare.lit', {'nick': 'some1'});
let room_els = _converse.rooms_list_view.el.querySelectorAll(".available-chatroom");
expect(room_els.length).toBe(1);
let item = room_els[0];
expect(u.hasClass('open', item)).toBe(true);
expect(item.textContent.trim()).toBe('coven@chat.shakespeare.lit');
_converse.api.rooms.open('balcony@chat.shakespeare.lit', {'nick': 'some1'});
room_els = _converse.rooms_list_view.el.querySelectorAll(".open-room");
expect(room_els.length).toBe(2);
room_els = _converse.rooms_list_view.el.querySelectorAll(".available-chatroom.open");
expect(room_els.length).toBe(1);
item = room_els[0];
expect(item.textContent.trim()).toBe('balcony@chat.shakespeare.lit');
done();
}));
it("has an info icon which opens a details modal when clicked", mock.initConverseWithPromises(
null, ['rosterGroupsFetched'],
@ -114,7 +145,7 @@
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");
const info_el = _converse.rooms_list_view.el.querySelector(".room-info");
info_el.click();
const modal = view.model.room_details_modal;
@ -122,8 +153,8 @@
}).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[0].textContent).toBe("Name: A Dark Cave")
expect(els[1].textContent).toBe("Room address (JID): coven@chat.shakespeare.lit")
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');
@ -149,8 +180,17 @@
els = modal.el.querySelectorAll('p.room-info');
expect(els[3].textContent).toBe("Online users: 2")
view.model.set({'subject': {'author': 'someone', 'text': 'Hatching dark plots'}});
els = modal.el.querySelectorAll('p.room-info');
expect(els[0].textContent).toBe("Name: A Dark Cave")
expect(els[1].textContent).toBe("Room address (JID): coven@chat.shakespeare.lit")
expect(els[2].textContent).toBe("Description: This is the description")
expect(els[3].textContent).toBe("Topic: Hatching dark plots")
expect(els[4].textContent).toBe("Topic author: someone")
expect(els[5].textContent).toBe("Online users: 2")
done();
});
}).catch(_.partial(_converse.log, _, Strophe.LogLevel.FATAL));
}));
it("can be closed", mock.initConverseWithPromises(
@ -173,7 +213,7 @@
var close_el = _converse.rooms_list_view.el.querySelector(".close-room");
close_el.click();
expect(window.confirm).toHaveBeenCalledWith(
'Are you sure you want to leave the room lounge@conference.shakespeare.lit?');
'Are you sure you want to leave the groupchat lounge@conference.shakespeare.lit?');
room_els = _converse.rooms_list_view.el.querySelectorAll(".open-room");
expect(room_els.length).toBe(0);
expect(_converse.chatboxes.length).toBe(1);
@ -206,7 +246,7 @@
type: 'groupchat'
}).c('body').t('foo').tree());
// If the user isn't mentioned, the counter doesn't get incremented, but the text of the room is bold
// If the user isn't mentioned, the counter doesn't get incremented, but the text of the groupchat is bold
var room_el = _converse.rooms_list_view.el.querySelector(
".available-chatroom"
);

View File

@ -40,9 +40,9 @@
return test_utils.waitUntil(() => view.model.vcard.get('fullname') === 'Max Frankfurter')
.then(function () {
expect(view.el.querySelector('.chat-msg-author').textContent).toBe('Max Frankfurter');
expect(view.el.querySelector('.chat-msg__author').textContent.trim()).toBe('Max Frankfurter');
var message_content = view.el.querySelector('.chat-msg-text');
var message_content = view.el.querySelector('.chat-msg__text');
expect(message_content.textContent).toBe(spoiler);
var spoiler_hint_el = view.el.querySelector('.spoiler-hint');
@ -79,9 +79,9 @@
var view = _converse.chatboxviews.get(sender_jid);
return test_utils.waitUntil(() => view.model.vcard.get('fullname') === 'Max Frankfurter')
.then(function () {
expect(_.includes(view.el.querySelector('.chat-msg-author').textContent, 'Max Frankfurter')).toBeTruthy();
expect(_.includes(view.el.querySelector('.chat-msg__author').textContent, 'Max Frankfurter')).toBeTruthy();
var message_content = view.el.querySelector('.chat-msg-text');
var message_content = view.el.querySelector('.chat-msg__text');
expect(message_content.textContent).toBe(spoiler);
var spoiler_hint_el = view.el.querySelector('.spoiler-hint');
@ -148,9 +148,9 @@
expect(body_el.textContent).toBe('This is the spoiler');
/* Test the HTML spoiler message */
expect(view.el.querySelector('.chat-msg-author').textContent).toBe('Max Mustermann');
expect(view.el.querySelector('.chat-msg__author').textContent.trim()).toBe('Max Mustermann');
var spoiler_msg_el = view.el.querySelector('.chat-msg-text.spoiler');
var spoiler_msg_el = view.el.querySelector('.chat-msg__text.spoiler');
expect(spoiler_msg_el.textContent).toBe('This is the spoiler');
expect(_.includes(spoiler_msg_el.classList, 'collapsed')).toBeTruthy();
@ -227,9 +227,9 @@
expect(body_el.textContent).toBe('This is the spoiler');
/* Test the HTML spoiler message */
expect(view.el.querySelector('.chat-msg-author').textContent).toBe('Max Mustermann');
expect(view.el.querySelector('.chat-msg__author').textContent.trim()).toBe('Max Mustermann');
var spoiler_msg_el = view.el.querySelector('.chat-msg-text.spoiler');
var spoiler_msg_el = view.el.querySelector('.chat-msg__text.spoiler');
expect(spoiler_msg_el.textContent).toBe('This is the spoiler');
expect(_.includes(spoiler_msg_el.classList, 'collapsed')).toBeTruthy();

View File

@ -67,7 +67,7 @@
{ __ } = _converse;
const bookmark_button = tpl_chatroom_bookmark_toggle(
_.assignIn(this.model.toJSON(), {
info_toggle_bookmark: __('Bookmark this room'),
info_toggle_bookmark: __('Bookmark this groupchat'),
bookmarked: this.model.get('bookmarked')
}));
const close_button = this.el.querySelector('.close-chatbox-button');
@ -143,10 +143,10 @@
body.insertAdjacentHTML(
'beforeend',
tpl_chatroom_bookmark_form({
heading: __('Bookmark this room'),
heading: __('Bookmark this groupchat'),
label_name: __('The name for this bookmark:'),
label_autojoin: __('Would you like this room to be automatically joined upon startup?'),
label_nick: __('What should your nickname for this room be?'),
label_autojoin: __('Would you like this groupchat to be automatically joined upon startup?'),
label_nick: __('What should your nickname for this groupchat be?'),
default_nick: this.model.get('nick'),
label_submit: __('Save'),
label_cancel: __('Cancel')
@ -410,13 +410,13 @@
'hidden': _converse.hide_open_bookmarks &&
_converse.chatboxes.where({'jid': this.model.get('jid')}).length,
'bookmarked': true,
'info_leave_room': __('Leave this room'),
'info_leave_room': __('Leave this groupchat'),
'info_remove': __('Remove this bookmark'),
'info_remove_bookmark': __('Unbookmark this room'),
'info_title': __('Show more information on this room'),
'info_remove_bookmark': __('Unbookmark this groupchat'),
'info_title': __('Show more information on this groupchat'),
'jid': this.model.get('jid'),
'name': Strophe.xmlunescape(this.model.get('name')),
'open_title': __('Click to open this room')
'open_title': __('Click to open this groupchat')
});
}
});

View File

@ -19,6 +19,8 @@
const { $msg, Backbone, Promise, Strophe, b64_sha1, moment, sizzle, utils, _ } = converse.env;
const u = converse.env.utils;
Strophe.addNamespace('MESSAGE_CORRECT', 'urn:xmpp:message-correct:0');
converse.plugins.add('converse-chatboxes', {
@ -215,9 +217,11 @@
}, false);
xhr.onerror = () => {
let message = __('Sorry, could not succesfully upload your file.');
let message;
if (xhr.responseText) {
message += ' ' + __('Your server\'s response: "%1$s"', xhr.responseText)
message = __('Sorry, could not succesfully upload your file. Your servers response: "%1$s"', xhr.responseText)
} else {
message = __('Sorry, could not succesfully upload your file.');
}
this.save({
'type': 'error',
@ -290,6 +294,23 @@
return this.vcard.get('fullname') || this.get('jid');
},
handleMessageCorrection (stanza) {
const replace = sizzle(`replace[xmlns="${Strophe.NS.MESSAGE_CORRECT}"]`, stanza).pop();
if (replace) {
const msgid = replace && replace.getAttribute('id') || stanza.getAttribute('id'),
message = msgid && this.messages.findWhere({msgid}),
older_versions = message.get('older_versions') || [];
older_versions.push(message.get('message'));
message.save({
'message': _converse.chatboxes.getMessageBody(stanza),
'older_versions': older_versions,
'edited': true
});
return true;
}
return false;
},
createMessageStanza (message) {
/* Given a _converse.Message Backbone.Model, return the XML
* stanza that represents it.
@ -301,7 +322,7 @@
'from': _converse.connection.jid,
'to': this.get('jid'),
'type': this.get('message_type'),
'id': message.get('msgid')
'id': message.get('edited') && _converse.connection.getUniqueId() || message.get('msgid'),
}).c('body').t(message.get('message')).up()
.c(_converse.ACTIVE, {'xmlns': Strophe.NS.CHATSTATES}).up();
@ -315,6 +336,12 @@
if (message.get('file')) {
stanza.c('x', {'xmlns': Strophe.NS.OUTOFBAND}).c('url').t(message.get('message')).up();
}
if (message.get('edited')) {
stanza.c('replace', {
'xmlns': Strophe.NS.MESSAGE_CORRECT,
'id': message.get('msgid')
}).up();
}
return stanza;
},
@ -357,8 +384,20 @@
* Parameters:
* (Message) message - The chat message
*/
const message = this.messages.create(attrs);
this.sendMessageStanza(this.createMessageStanza(message));
let message = this.messages.findWhere('correcting')
if (message) {
const older_versions = message.get('older_versions') || [];
older_versions.push(message.get('message'));
message.save({
'message': attrs.message,
'older_versions': older_versions,
'edited': true,
'correcting': false
});
} else {
message = this.messages.create(attrs);
}
return this.sendMessageStanza(this.createMessageStanza(message));
},
sendChatState () {
@ -410,24 +449,12 @@
}).catch(_.partial(_converse.log, _, Strophe.LogLevel.FATAL));
},
getMessageBody (message) {
const type = message.getAttribute('type');
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, original_stanza) {
getMessageAttributesFromStanza (stanza, original_stanza) {
/* Parses a passed in message stanza and returns an object
* of attributes.
*
* Parameters:
* (XMLElement) message - The message stanza
* (XMLElement) stanza - The message stanza
* (XMLElement) delay - The <delay> node from the
* stanza, if there was one.
* (XMLElement) original_stanza - The original stanza,
@ -439,24 +466,24 @@
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 ||
message.getElementsByTagName(_converse.ACTIVE).length && _converse.ACTIVE ||
message.getElementsByTagName(_converse.GONE).length && _converse.GONE;
chat_state = stanza.getElementsByTagName(_converse.COMPOSING).length && _converse.COMPOSING ||
stanza.getElementsByTagName(_converse.PAUSED).length && _converse.PAUSED ||
stanza.getElementsByTagName(_converse.INACTIVE).length && _converse.INACTIVE ||
stanza.getElementsByTagName(_converse.ACTIVE).length && _converse.ACTIVE ||
stanza.getElementsByTagName(_converse.GONE).length && _converse.GONE;
const attrs = {
'chat_state': chat_state,
'is_archived': !_.isNil(archive),
'is_delayed': !_.isNil(delay),
'is_spoiler': !_.isNil(spoiler),
'message': this.getMessageBody(message) || undefined,
'msgid': message.getAttribute('id'),
'message': _converse.chatboxes.getMessageBody(stanza) || undefined,
'msgid': stanza.getAttribute('id'),
'time': delay ? delay.getAttribute('stamp') : moment().format(),
'type': message.getAttribute('type')
'type': stanza.getAttribute('type')
};
if (attrs.type === 'groupchat') {
attrs.from = message.getAttribute('from');
attrs.from = stanza.getAttribute('from');
attrs.nick = Strophe.unescapeNode(Strophe.getResourceFromJid(attrs.from));
if (attrs.from === this.get('nick')) {
attrs.sender = 'me';
@ -464,7 +491,7 @@
attrs.sender = 'them';
}
} else {
attrs.from = Strophe.getBareJidFromJid(message.getAttribute('from'));
attrs.from = Strophe.getBareJidFromJid(stanza.getAttribute('from'));
if (attrs.from === _converse.bare_jid) {
attrs.sender = 'me';
attrs.fullname = _converse.xmppstatus.get('fullname');
@ -473,7 +500,7 @@
attrs.fullname = this.get('fullname');
}
}
_.each(sizzle(`x[xmlns="${Strophe.NS.OUTOFBAND}"]`, message), (xform) => {
_.each(sizzle(`x[xmlns="${Strophe.NS.OUTOFBAND}"]`, stanza), (xform) => {
attrs['oob_url'] = xform.querySelector('url').textContent;
attrs['oob_desc'] = xform.querySelector('url').textContent;
});
@ -591,19 +618,29 @@
return true;
},
onMessage (message) {
getMessageBody (stanza) {
/* Given a message stanza, return the text contained in its body.
*/
const type = stanza.getAttribute('type');
if (type === 'error') {
const error = stanza.querySelector('error');
return _.propertyOf(error.querySelector('text'))('textContent') ||
__('Sorry, an error occurred:') + ' ' + error.innerHTML;
} else {
return _.propertyOf(stanza.querySelector('body'))('textContent');
}
},
onMessage (stanza) {
/* Handler method for all incoming single-user chat "message"
* stanzas.
*
* Parameters:
* (XMLElement) message - The incoming message stanza
* (XMLElement) stanza - The incoming message stanza
*/
let from_jid = message.getAttribute('from'),
to_jid = message.getAttribute('to');
const original_stanza = message,
to_resource = Strophe.getResourceFromJid(to_jid),
is_carbon = !_.isNull(message.querySelector(`received[xmlns="${Strophe.NS.CARBONS}"]`));
let from_jid = stanza.getAttribute('from'),
to_jid = stanza.getAttribute('to');
const to_resource = Strophe.getResourceFromJid(to_jid);
if (_converse.filter_by_resource && (to_resource && to_resource !== _converse.resource)) {
_converse.log(
@ -611,7 +648,7 @@
Strophe.LogLevel.INFO
);
return true;
} else if (utils.isHeadlineMessage(_converse, message)) {
} else if (utils.isHeadlineMessage(_converse, stanza)) {
// XXX: Ideally we wouldn't have to check for headline
// messages, but Prosody sends headline messages with the
// wrong type ('chat'), so we need to filter them out here.
@ -621,18 +658,23 @@
);
return true;
}
const forwarded = message.querySelector('forwarded');
const forwarded = stanza.querySelector('forwarded'),
original_stanza = stanza;
if (!_.isNull(forwarded)) {
const forwarded_message = forwarded.querySelector('message');
const forwarded_from = forwarded_message.getAttribute('from');
const forwarded_message = forwarded.querySelector('message'),
forwarded_from = forwarded_message.getAttribute('from'),
is_carbon = !_.isNull(stanza.querySelector(`received[xmlns="${Strophe.NS.CARBONS}"]`));
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;
from_jid = message.getAttribute('from');
to_jid = message.getAttribute('to');
stanza = forwarded_message;
from_jid = stanza.getAttribute('from');
to_jid = stanza.getAttribute('to');
}
const from_bare_jid = Strophe.getBareJidFromJid(from_jid),
@ -650,16 +692,14 @@
const attrs = {
'fullname': _.get(_converse.api.contacts.get(contact_jid), 'attributes.fullname')
}
const chatbox = this.getChatBox(contact_jid, attrs, !_.isNull(message.querySelector('body'))),
msgid = message.getAttribute('id');
if (chatbox) {
const messages = msgid && chatbox.messages.findWhere({msgid}) || [];
if (_.isEmpty(messages)) {
// Only create the message when we're sure it's not a
// duplicate
const chatbox = this.getChatBox(contact_jid, attrs, !_.isNull(stanza.querySelector('body')));
if (chatbox && !chatbox.handleMessageCorrection(stanza)) {
const msgid = stanza.getAttribute('id'),
message = msgid && chatbox.messages.findWhere({msgid});
if (!message) {
// Only create the message when we're sure it's not a duplicate
chatbox.incrementUnreadMsgCounter(original_stanza);
chatbox.createMessage(message, original_stanza);
chatbox.createMessage(stanza, original_stanza);
}
}
_converse.emit('message', {'stanza': original_stanza, 'chatbox': chatbox});
@ -813,6 +853,7 @@
_converse.on('addClientFeatures', () => {
_converse.api.disco.own.features.add(Strophe.NS.MESSAGE_CORRECT);
_converse.api.disco.own.features.add(Strophe.NS.HTTPUPLOAD);
_converse.api.disco.own.features.add(Strophe.NS.OUTOFBAND);
});

View File

@ -9,7 +9,6 @@
"bootstrap",
"emojione",
"xss",
"templates/action.html",
"templates/chatbox.html",
"templates/chatbox_head.html",
"templates/chatbox_message_form.html",
@ -33,7 +32,6 @@
bootstrap,
emojione,
xss,
tpl_action,
tpl_chatbox,
tpl_chatbox_head,
tpl_chatbox_message_form,
@ -54,6 +52,8 @@
const u = converse.env.utils;
const KEY = {
ENTER: 13,
UP_ARROW: 38,
DOWN_ARROW: 40,
FORWARD_SLASH: 47
};
@ -321,6 +321,7 @@
events: {
'change input.fileupload': 'onFileSelection',
'click .chat-msg__action-edit': 'onMessageEditButtonClicked',
'click .chatbox-navback': 'showControlBox',
'click .close-chatbox-button': 'close',
'click .new-msgs-indicator': 'viewUnreadMessages',
@ -333,8 +334,8 @@
'click .toggle-smiley ul.emoji-picker li': 'insertEmoji',
'click .toggle-smiley': 'toggleEmojiMenu',
'click .upload-file': 'toggleFileUpload',
'keypress .chat-textarea': 'keyPressed',
'input .chat-textarea': 'inputChanged'
'input .chat-textarea': 'inputChanged',
'keydown .chat-textarea': 'keyPressed'
},
initialize () {
@ -746,19 +747,19 @@
date = moment(el.getAttribute('data-isodate')),
next_el = el.nextElementSibling;
if (!u.hasClass('chat-action', el) && !u.hasClass('chat-action', previous_el) &&
if (!u.hasClass('chat-msg--action', el) && !u.hasClass('chat-msg--action', previous_el) &&
previous_el.getAttribute('data-from') === from &&
date.isBefore(moment(previous_el.getAttribute('data-isodate')).add(10, 'minutes'))) {
u.addClass('chat-msg-followup', el);
u.addClass('chat-msg--followup', el);
}
if (!next_el) { return; }
if (!u.hasClass('chat-action', 'el') &&
if (!u.hasClass('chat-msg--action', 'el') &&
next_el.getAttribute('data-from') === from &&
moment(next_el.getAttribute('data-isodate')).isBefore(date.add(10, 'minutes'))) {
u.addClass('chat-msg-followup', next_el);
u.addClass('chat-msg--followup', next_el);
} else {
u.removeClass('chat-msg-followup', next_el);
u.removeClass('chat-msg--followup', next_el);
}
},
@ -802,7 +803,9 @@
* (Object) message - The message Backbone object that was added.
*/
this.showMessage(message);
if (message.get('correcting')) {
this.insertIntoTextArea(message.get('message'), true);
}
_converse.emit('messageAdded', {
'message': message,
'chatbox': this.model
@ -911,8 +914,14 @@
keyPressed (ev) {
/* Event handler for when a key is pressed in a chat box textarea.
*/
if (ev.keyCode === KEY.ENTER && !ev.shiftKey) {
if (ev.shiftKey) { return; }
if (ev.keyCode === KEY.ENTER) {
this.onFormSubmitted(ev);
} else if (ev.keyCode === KEY.UP_ARROW && !ev.target.selectionEnd) {
this.editEarlierMessage();
} else if (ev.keyCode === KEY.DOWN_ARROW && ev.target.selectionEnd === ev.target.value.length) {
this.editLaterMessage();
} 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
// (which would imply an internal command and not a message).
@ -920,6 +929,71 @@
}
},
getOwnMessages () {
return f(this.model.messages.filter({'sender': 'me'}));
},
onMessageEditButtonClicked (ev) {
const idx = this.model.messages.findLastIndex('correcting'),
currently_correcting = idx >=0 ? this.model.messages.at(idx) : null,
message_el = u.ancestor(ev.target, '.chat-msg'),
message = this.model.messages.findWhere({'msgid': message_el.getAttribute('data-msgid')});
if (currently_correcting !== message) {
if (!_.isNil(currently_correcting)) {
currently_correcting.save('correcting', false);
}
message.save('correcting', true);
this.insertIntoTextArea(message.get('message'), true);
} else {
message.save('correcting', false);
this.insertIntoTextArea('', true);
}
},
editLaterMessage () {
let message;
let idx = this.model.messages.findLastIndex('correcting');
if (idx >= 0) {
this.model.messages.at(idx).save('correcting', false);
while (idx < this.model.messages.length-1) {
idx += 1;
const candidate = this.model.messages.at(idx);
if (candidate.get('sender') === 'me' && candidate.get('message')) {
message = candidate;
break;
}
}
}
if (message) {
this.insertIntoTextArea(message.get('message'), true);
message.save('correcting', true);
} else {
this.insertIntoTextArea('', true);
}
},
editEarlierMessage () {
let message;
let idx = this.model.messages.findLastIndex('correcting');
if (idx >= 0) {
this.model.messages.at(idx).save('correcting', false);
while (idx > 0) {
idx -= 1;
const candidate = this.model.messages.at(idx);
if (candidate.get('sender') === 'me' && candidate.get('message')) {
message = candidate;
break;
}
}
}
message = message || this.getOwnMessages().findLast((msg) => msg.get('message'));
if (message) {
this.insertIntoTextArea(message.get('message'), true);
message.save('correcting', true);
}
},
inputChanged (ev) {
ev.target.style.height = 'auto'; // Fixes weirdness
ev.target.style.height = (ev.target.scrollHeight) + 'px';
@ -936,14 +1010,18 @@
return this;
},
insertIntoTextArea (value) {
const textbox_el = this.el.querySelector('.chat-textarea');
let existing = textbox_el.value;
insertIntoTextArea (value, replace=false) {
const textarea = this.el.querySelector('.chat-textarea');
if (replace) {
textarea.value = value;
} else {
let existing = textarea.value;
if (existing && (existing[existing.length-1] !== ' ')) {
existing = existing + ' ';
}
textbox_el.value = existing+value+' ';
textbox_el.focus()
textarea.value = existing+value+' ';
}
textarea.focus()
},
createEmojiPicker () {
@ -1017,13 +1095,13 @@
let text;
if (u.isVisible(this.el)) {
if (show === 'offline') {
text = fullname+' '+__('has gone offline');
text = __('%1$s has gone offline', fullname);
} else if (show === 'away') {
text = fullname+' '+__('has gone away');
text = __('%1$s has gone away', fullname);
} else if ((show === 'dnd')) {
text = fullname+' '+__('is busy');
text = __('%1$s is busy', fullname);
} else if (show === 'online') {
text = fullname+' '+__('is online');
text = __('%1$s is online', fullname);
}
if (text) {
this.content.insertAdjacentHTML(

View File

@ -200,8 +200,6 @@
_converse.api.promises.add('controlboxInitialized');
const LABEL_CONTACTS = __('Contacts');
_converse.addControlBox = () =>
_converse.chatboxes.add({
id: 'controlbox',
@ -269,6 +267,9 @@
},
insertRoster () {
if (_converse.authentication === _converse.ANONYMOUS) {
return;
}
/* Place the rosterview inside the "Contacts" panel. */
_converse.api.waitUntil('rosterViewInitialized')
.then(() => this.controlbox_pane.el.insertAdjacentElement('beforeEnd', _converse.rosterview.el))
@ -470,7 +471,11 @@
let jid = form_data.get('jid');
if (_converse.locked_domain) {
jid = Strophe.escapeNode(jid) + '@' + _converse.locked_domain;
const last_part = '@' + _converse.locked_domain;
if (jid.endsWith(last_part)) {
jid = jid.substr(0, jid.length - last_part.length);
}
jid = Strophe.escapeNode(jid) + last_part;
} else if (_converse.default_domain && !_.includes(jid, '@')) {
jid = jid + '@' + _converse.default_domain;
}

View File

@ -228,6 +228,8 @@
}
if (message instanceof Error) {
message = message.stack;
} else if (_.isElement(message)) {
message = message.outerHTML;
}
const prefix = style ? '%c' : '';
const logger = _.assign({
@ -294,6 +296,10 @@
}
};
_converse.isSingleton = function () {
return _.includes(['mobile', 'fullscreen', 'embedded'], _converse.view_mode);
}
_converse.router = new Backbone.Router();
@ -735,8 +741,8 @@
this.connection.addHandler((iq) => {
if (iq.querySelectorAll('error').length > 0) {
_converse.log(
'An error occured while trying to enable message carbons.',
Strophe.LogLevel.ERROR);
'An error occurred while trying to enable message carbons.',
Strophe.LogLevel.WARN);
} else {
this.session.save({'carbons_enabled': true});
_converse.log('Message carbons have been enabled.');

View File

@ -10,24 +10,22 @@
"xss",
"emojione",
"filesize",
"templates/action.html",
"templates/csn.html",
"templates/file_progress.html",
"templates/info.html",
"templates/message.html",
"templates/spoiler_message.html"
"templates/message_versions_modal.html",
], factory);
}(this, function (
converse,
xss,
emojione,
filesize,
tpl_action,
tpl_csn,
tpl_file_progress,
tpl_info,
tpl_message,
tpl_spoiler_message
tpl_message_versions_modal
) {
"use strict";
const { Backbone, _, moment } = converse.env;
@ -69,10 +67,27 @@
},
});
_converse.MessageVersionsModal = _converse.BootstrapModal.extend({
toHTML () {
return tpl_message_versions_modal(_.extend(
this.model.toJSON(), {
'__': __
}));
}
});
_converse.MessageView = _converse.ViewWithAvatar.extend({
events: {
'click .chat-msg__edit-modal': 'showMessageVersionsModal'
},
initialize () {
this.model.vcard.on('change', this.render, this);
this.model.on('change:correcting', this.onMessageCorrection, this);
this.model.on('change:message', this.render, this);
this.model.on('change:progress', this.renderFileUploadProgresBar, this);
this.model.on('change:type', this.render, this);
this.model.on('change:upload', this.render, this);
@ -81,7 +96,7 @@
},
render () {
const is_followup = u.hasClass('chat-msg-followup', this.el);
const is_followup = u.hasClass('chat-msg--followup', this.el);
let msg;
if (this.model.isOnlyChatStateNotification()) {
this.renderChatStateNotification()
@ -93,11 +108,19 @@
this.renderChatMessage();
}
if (is_followup) {
u.addClass('chat-msg-followup', this.el);
u.addClass('chat-msg--followup', this.el);
}
return this.el;
},
onMessageCorrection () {
this.render();
if (!this.model.get('correcting') && this.model.changed.message) {
this.el.addEventListener('animationend', () => u.removeClass('onload', this.el));
u.addClass('onload', this.el);
}
},
replaceElement (msg) {
if (!_.isNil(this.el.parentElement)) {
this.el.parentElement.replaceChild(msg, this.el);
@ -107,20 +130,16 @@
},
renderChatMessage () {
let template, text = this.model.get('message');
if (this.isMeCommand()) {
template = tpl_action;
text = this.model.get('message').replace(/^\/me/, '');
} else {
template = this.model.get('is_spoiler') ? tpl_spoiler_message : tpl_message;
}
const moment_time = moment(this.model.get('time')),
const is_me_message = this.isMeCommand(),
moment_time = moment(this.model.get('time')),
role = this.model.vcard.get('role'),
roles = role ? role.split(',') : [];
const msg = u.stringToElement(template(
const msg = u.stringToElement(tpl_message(
_.extend(
this.model.toJSON(), {
'__': __,
'is_me_message': is_me_message,
'roles': roles,
'pretty_time': moment_time.format(_converse.time_format),
'time': moment_time.format(),
@ -130,16 +149,20 @@
})
));
var url = this.model.get('oob_url');
const url = this.model.get('oob_url');
if (url) {
msg.querySelector('.chat-msg-media').innerHTML = _.flow(
msg.querySelector('.chat-msg__media').innerHTML = _.flow(
_.partial(u.renderFileURL, _converse),
_.partial(u.renderMovieURL, _converse),
_.partial(u.renderAudioURL, _converse),
_.partial(u.renderImageURL, _converse))(url);
}
const msg_content = msg.querySelector('.chat-msg-text');
let text = this.model.get('message');
if (is_me_message) {
text = text.replace(/^\/me/, '');
}
const msg_content = msg.querySelector('.chat-msg__text');
if (text !== url) {
text = xss.filterXSS(text, {'whiteList': {}});
msg_content.innerHTML = _.flow(
@ -179,16 +202,16 @@
if (this.model.get('sender') === 'me') {
text = __('Typing from another device');
} else {
text = name +' '+__('is typing');
text = __('%1$s is typing', name);
}
} else if (this.model.get('chat_state') === _converse.PAUSED) {
if (this.model.get('sender') === 'me') {
text = __('Stopped typing on the other device');
} else {
text = name +' '+__('has stopped typing');
text = __('%1$s has stopped typing', name);
}
} else if (this.model.get('chat_state') === _converse.GONE) {
text = name +' '+__('has gone away');
text = __('%1$s has gone away', name);
} else {
return;
}
@ -211,6 +234,14 @@
this.renderAvatar();
},
showMessageVersionsModal (ev) {
ev.preventDefault();
if (_.isUndefined(this.model.message_versions_modal)) {
this.model.message_versions_modal = new _converse.MessageVersionsModal({'model': this.model});
}
this.model.message_versions_modal.show(ev);
},
isMeCommand () {
const text = this.model.get('message');
if (!text) {
@ -234,6 +265,9 @@
extra_classes += ' mentioned';
}
}
if (this.model.get('correcting')) {
extra_classes += ' correcting';
}
return extra_classes;
}
});

View File

@ -8,6 +8,7 @@
define([
"converse-core",
"utils/muc",
"xss",
"templates/add_chatroom_modal.html",
"templates/chatarea.html",
"templates/chatroom.html",
@ -35,6 +36,7 @@
}(this, function (
converse,
muc_utils,
xss,
tpl_add_chatroom_modal,
tpl_chatarea,
tpl_chatroom,
@ -196,24 +198,24 @@
*/
_converse.muc = {
info_messages: {
100: __('This room is not anonymous'),
102: __('This room now shows unavailable members'),
103: __('This room does not show unavailable members'),
104: __('The room configuration has changed'),
170: __('Room logging is now enabled'),
171: __('Room logging is now disabled'),
172: __('This room is now no longer anonymous'),
173: __('This room is now semi-anonymous'),
174: __('This room is now fully-anonymous'),
201: __('A new room has been created')
100: __('This groupchat is not anonymous'),
102: __('This groupchat now shows unavailable members'),
103: __('This groupchat does not show unavailable members'),
104: __('The groupchat configuration has changed'),
170: __('groupchat logging is now enabled'),
171: __('groupchat logging is now disabled'),
172: __('This groupchat is now no longer anonymous'),
173: __('This groupchat is now semi-anonymous'),
174: __('This groupchat is now fully-anonymous'),
201: __('A new groupchat has been created')
},
disconnect_messages: {
301: __('You have been banned from this room'),
307: __('You have been kicked from this room'),
321: __("You have been removed from this room because of an affiliation change"),
322: __("You have been removed from this room because the room has changed to members-only and you're not a member"),
332: __("You have been removed from this room because the MUC (Multi-user chat) service is being shut down")
301: __('You have been banned from this groupchat'),
307: __('You have been kicked from this groupchat'),
321: __("You have been removed from this groupchat because of an affiliation change"),
322: __("You have been removed from this groupchat because the groupchat has changed to members-only and you're not a member"),
332: __("You have been removed from this groupchat because the MUC (Multi-user chat) service is being shut down")
},
action_info_messages: {
@ -271,19 +273,19 @@
'temporary': sizzle('feature[var="muc_temporary"]', stanza).length,
'unmoderated': sizzle('feature[var="muc_unmoderated"]', stanza).length,
'label_desc': __('Description:'),
'label_jid': __('Room Address (JID):'),
'label_occ': __('Occupants:'),
'label_jid': __('Groupchat Address (JID):'),
'label_occ': __('Participants:'),
'label_features': __('Features:'),
'label_requires_auth': __('Requires authentication'),
'label_hidden': __('Hidden'),
'label_requires_invite': __('Requires an invitation'),
'label_moderated': __('Moderated'),
'label_non_anon': __('Non-anonymous'),
'label_open_room': __('Open room'),
'label_permanent_room': __('Permanent room'),
'label_open_room': __('Open'),
'label_permanent_room': __('Permanent'),
'label_public': __('Public'),
'label_semi_anon': __('Semi-anonymous'),
'label_temp_room': __('Temporary room'),
'label_temp_room': __('Temporary'),
'label_unmoderated': __('Unmoderated')
}));
}
@ -321,7 +323,7 @@
toHTML () {
return tpl_list_chatrooms_modal(_.extend(this.model.toJSON(), {
'heading_list_chatrooms': __('Query for Chatrooms'),
'heading_list_chatrooms': __('Query for Groupchats'),
'label_server_address': __('Server address'),
'label_query': __('Show rooms'),
'server_placeholder': __('conference.example.org')
@ -362,8 +364,8 @@
div.innerHTML = tpl_room_item({
'name': Strophe.xmlunescape(name),
'jid': room.getAttribute('jid'),
'open_title': __('Click to open this room'),
'info_title': __('Show more information on this room')
'open_title': __('Click to open this groupchat'),
'info_title': __('Show more information on this groupchat')
});
return div.firstElementChild;
},
@ -447,8 +449,8 @@
toHTML () {
return tpl_add_chatroom_modal(_.extend(this.model.toJSON(), {
'heading_new_chatroom': __('Enter a new Chatroom'),
'label_room_address': __('Room address'),
'heading_new_chatroom': __('Enter a new Groupchat'),
'label_room_address': __('Groupchat address'),
'label_nickname': __('Optional nickname'),
'chatroom_placeholder': __('name@conference.example.org'),
'label_join': __('Join'),
@ -493,8 +495,10 @@
toHTML () {
return tpl_chatroom_details_modal(_.extend(
this.model.toJSON(), {
'_': _,
'__': __,
'display_name': this.model.getDisplayName(),
'topic': u.addHyperlinks(xss.filterXSS(_.get(this.model.get('subject'), 'text'), {'whiteList': {}})),
'display_name': __('Groupchat info for %1$s', this.model.getDisplayName()),
'num_occupants': this.model.occupants.length
})
);
@ -525,7 +529,7 @@
'click .toggle-smiley ul.emoji-picker li': 'insertEmoji',
'click .toggle-smiley': 'toggleEmojiMenu',
'click .upload-file': 'toggleFileUpload',
'keypress .chat-textarea': 'keyPressed',
'keydown .chat-textarea': 'keyPressed',
'input .chat-textarea': 'inputChanged'
},
@ -601,13 +605,13 @@
*/
if (_.isNull(this.el.querySelector('.chat-area'))) {
const container_el = this.el.querySelector('.chatroom-body');
container_el.innerHTML = tpl_chatarea({
container_el.insertAdjacentHTML('beforeend', tpl_chatarea({
'label_message': __('Message'),
'label_send': __('Send'),
'show_send_button': _converse.show_send_button,
'show_toolbar': _converse.show_toolbar,
'unread_msgs': __('You have unread messages')
});
}));
container_el.insertAdjacentElement('beforeend', this.occupantsview.el);
this.renderToolbar(tpl_chatroom_toolbar);
this.content = this.el.querySelector('.chat-content');
@ -662,9 +666,9 @@
return tpl_chatroom_head(
_.extend(this.model.toJSON(), {
'Strophe': Strophe,
'info_close': __('Close and leave this room'),
'info_configure': __('Configure this room'),
'info_details': __('Show more details about this room'),
'info_close': __('Close and leave this groupchat'),
'info_configure': __('Configure this groupchat'),
'info_details': __('Show more details about this groupchat'),
'description': this.model.get('description') || ''
}));
},
@ -708,7 +712,7 @@
return _.extend(
_converse.ChatBoxView.prototype.getToolbarOptions.apply(this, arguments),
{
label_hide_occupants: __('Hide the list of occupants'),
label_hide_occupants: __('Hide the list of participants'),
show_occupants_toggle: this.is_chatroom && _converse.visible_toolbar_buttons.toggle_occupants
}
);
@ -867,20 +871,20 @@
case 'help':
this.showHelpMessages([
`<strong>/admin</strong>: ${__("Change user's affiliation to admin")}`,
`<strong>/ban</strong>: ${__('Ban user from room')}`,
`<strong>/ban</strong>: ${__('Ban user from groupchat')}`,
`<strong>/clear</strong>: ${__('Remove messages')}`,
`<strong>/deop</strong>: ${__('Change user role to participant')}`,
`<strong>/help</strong>: ${__('Show this menu')}`,
`<strong>/kick</strong>: ${__('Kick user from room')}`,
`<strong>/kick</strong>: ${__('Kick user from groupchat')}`,
`<strong>/me</strong>: ${__('Write in 3rd person')}`,
`<strong>/member</strong>: ${__('Grant membership to a user')}`,
`<strong>/mute</strong>: ${__("Remove user's ability to post messages")}`,
`<strong>/nick</strong>: ${__('Change your nickname')}`,
`<strong>/op</strong>: ${__('Grant moderator role to user')}`,
`<strong>/owner</strong>: ${__('Grant ownership of this room')}`,
`<strong>/owner</strong>: ${__('Grant ownership of this groupchat')}`,
`<strong>/revoke</strong>: ${__("Revoke user's membership")}`,
`<strong>/subject</strong>: ${__('Set room subject')}`,
`<strong>/topic</strong>: ${__('Set room subject (alias for /subject)')}`,
`<strong>/subject</strong>: ${__('Set groupchat subject')}`,
`<strong>/topic</strong>: ${__('Set groupchat subject (alias for /subject)')}`,
`<strong>/voice</strong>: ${__('Allow muted user to post messages')}`
]);
break;
@ -1044,7 +1048,7 @@
fieldset_el.insertAdjacentHTML('beforeend', `<legend>${title}</legend>`);
if (instructions && instructions !== title) {
fieldset_el.insertAdjacentHTML('beforeend', `<p class="instructions">${instructions}</p>`);
fieldset_el.insertAdjacentHTML('beforeend', `<p class="form-help">${instructions}</p>`);
}
_.each(fields, function (field) {
fieldset_el.insertAdjacentHTML('beforeend', u.xForm2webForm(field, stanza));
@ -1226,7 +1230,7 @@
tpl_chatroom_nickname_form({
heading: __('Please choose your nickname'),
label_nickname: __('Nickname'),
label_join: __('Enter room'),
label_join: __('Enter groupchat'),
validation_message: message
}));
this.model.save('connection_status', converse.ROOMSTATUS.NICKNAME_REQUIRED);
@ -1249,7 +1253,7 @@
container_el.insertAdjacentHTML('beforeend',
tpl_chatroom_password_form({
heading: __('This chatroom requires a password'),
heading: __('This groupchat requires a password'),
label_password: __('Password: '),
label_submit: __('Submit')
}));
@ -1259,16 +1263,19 @@
'submit', this.submitPassword.bind(this), false);
},
showDisconnectMessage (msg) {
showDisconnectMessages (msgs) {
if (_.isString(msgs)) {
msgs = [msgs];
}
u.hideElement(this.el.querySelector('.chat-area'));
u.hideElement(this.el.querySelector('.occupants'));
_.each(this.el.querySelectorAll('.spinner'), u.removeElement);
this.el.querySelector('.chatroom-body').insertAdjacentHTML(
'beforeend',
tpl_chatroom_disconnect({
'disconnect_message': msg
const container = this.el.querySelector('.disconnect-container');
container.innerHTML = tpl_chatroom_disconnect({
'_': _,
'disconnect_messages': msgs
})
);
u.showElement(container);
},
getMessageFromStatus (stat, stanza, is_self) {
@ -1346,13 +1353,15 @@
* information to the user.
*/
if (notification.disconnected) {
this.showDisconnectMessage(notification.disconnection_message);
const messages = [];
messages.push(notification.disconnection_message);
if (notification.actor) {
this.showDisconnectMessage(__('This action was done by %1$s.', notification.actor));
messages.push(__('This action was done by %1$s.', notification.actor));
}
if (notification.reason) {
this.showDisconnectMessage(__('The reason given is: "%1$s".', notification.reason));
messages.push(__('The reason given is: "%1$s".', notification.reason));
}
this.showDisconnectMessages(messages);
this.model.save('connection_status', converse.ROOMSTATUS.DISCONNECTED);
return;
}
@ -1389,14 +1398,14 @@
'data': `data-leavejoin="${nick}"`,
'isodate': moment().format(),
'extra_classes': 'chat-event',
'message': __('%1$s has left and re-entered the room', nick)
'message': __('%1$s has left and re-entered the groupchat', nick)
});
} else {
let message;
if (_.isNil(stat)) {
message = __('%1$s has entered the room', nick);
message = __('%1$s has entered the groupchat', nick);
} else {
message = __('%1$s has entered the room. "%2$s"', nick, stat);
message = __('%1$s has entered the groupchat. "%2$s"', nick, stat);
}
const data = {
'data': `data-join="${nick}"`,
@ -1429,9 +1438,9 @@
let message;
if (_.isNil(stat)) {
message = __('%1$s has entered and left the room', nick);
message = __('%1$s has entered and left the groupchat', nick);
} else {
message = __('%1$s has entered and left the room. "%2$s"', nick, stat);
message = __('%1$s has entered and left the groupchat. "%2$s"', nick, stat);
}
last_el.outerHTML =
tpl_info({
@ -1443,9 +1452,9 @@
} else {
let message;
if (_.isNil(stat)) {
message = __('%1$s has left the room', nick);
message = __('%1$s has left the groupchat', nick);
} else {
message = __('%1$s has left the room. "%2$s"', nick, stat);
message = __('%1$s has left the groupchat. "%2$s"', nick, stat);
}
const data = {
'message': message,
@ -1488,25 +1497,32 @@
if (!_.isNull(error.querySelector('not-authorized'))) {
this.renderPasswordForm();
} else if (!_.isNull(error.querySelector('registration-required'))) {
this.showDisconnectMessage(__('You are not on the member list of this room.'));
this.showDisconnectMessages(__('You are not on the member list of this groupchat.'));
} else if (!_.isNull(error.querySelector('forbidden'))) {
this.showDisconnectMessage(__('You have been banned from this room.'));
this.showDisconnectMessages(__('You have been banned from this groupchat.'));
}
} else if (error.getAttribute('type') === 'modify') {
if (!_.isNull(error.querySelector('jid-malformed'))) {
this.showDisconnectMessage(__('No nickname was specified.'));
this.showDisconnectMessages(__('No nickname was specified.'));
}
} else if (error.getAttribute('type') === 'cancel') {
if (!_.isNull(error.querySelector('not-allowed'))) {
this.showDisconnectMessage(__('You are not allowed to create new rooms.'));
this.showDisconnectMessages(__('You are not allowed to create new rooms.'));
} else if (!_.isNull(error.querySelector('not-acceptable'))) {
this.showDisconnectMessage(__("Your nickname doesn't conform to this room's policies."));
this.showDisconnectMessages(__("Your nickname doesn't conform to this groupchat's policies."));
} else if (!_.isNull(error.querySelector('conflict'))) {
this.onNicknameClash(presence);
} else if (!_.isNull(error.querySelector('item-not-found'))) {
this.showDisconnectMessage(__("This room does not (yet) exist."));
this.showDisconnectMessages(__("This groupchat does not (yet) exist."));
} else if (!_.isNull(error.querySelector('service-unavailable'))) {
this.showDisconnectMessage(__("This room has reached its maximum number of occupants."));
this.showDisconnectMessages(__("This groupchat has reached its maximum number of participants."));
} else if (!_.isNull(error.querySelector('remote-server-not-found'))) {
const messages = [__("Remote server not found")];
const reason = _.get(error.querySelector('text'), 'textContent');
if (reason) {
messages.push(__('The explanation given is: "%1$s".', reason));
}
this.showDisconnectMessages(messages);
}
}
},
@ -1589,7 +1605,7 @@
render () {
this.el.innerHTML = tpl_room_panel({
'heading_chatrooms': __('Chatrooms'),
'heading_chatrooms': __('Groupchats'),
'title_new_room': __('Add a new room'),
'title_list_rooms': __('Query for rooms')
});
@ -1630,8 +1646,8 @@
'hint_show': _converse.PRETTY_CHAT_STATUS[show],
'hint_occupant': __('Click to mention %1$s in your message.', this.model.get('nick')),
'desc_moderator': __('This user is a moderator.'),
'desc_participant': __('This user can send messages in this room.'),
'desc_visitor': __('This user can NOT send messages in this room.'),
'desc_participant': __('This user can send messages in this groupchat.'),
'desc_visitor': __('This user can NOT send messages in this groupchat.'),
'label_moderator': __('Moderator'),
'label_visitor': __('Visitor'),
'label_owner': __('Owner'),
@ -1688,7 +1704,7 @@
this.el.innerHTML = tpl_chatroom_sidebar(
_.extend(this.chatroomview.model.toJSON(), {
'allow_muc_invitations': _converse.allow_muc_invitations,
'label_occupants': __('Occupants')
'label_occupants': __('Participants')
})
);
if (_converse.allow_muc_invitations) {

View File

@ -204,7 +204,7 @@
this.onPresence(stanza);
return true;
},
Strophe.NS.MUC, 'presence', null, null, room_jid,
null, 'presence', null, null, room_jid,
{'ignoreNamespaceFragment': true, 'matchBareFromJid': true}
);
this.message_handler = _converse.connection.addHandler((stanza) => {
@ -331,8 +331,9 @@
this.parseRoomFeatures(stanza);
resolve()
}).catch((err) => {
_converse.log(err, Strophe.LogLevel.ERROR);
reject(new Error("Could not parse the room features"));
_converse.log("Could not parse the room features", Strophe.LogLevel.WARN);
_converse.log(err, Strophe.LogLevel.WARN);
reject(err);
});
});
},
@ -848,14 +849,15 @@
if (!_.isNull(forwarded)) {
stanza = forwarded.querySelector('message');
}
const jid = stanza.getAttribute('from'),
resource = Strophe.getResourceFromJid(jid),
sender = resource && Strophe.unescapeNode(resource) || '',
subject = _.propertyOf(stanza.querySelector('subject'))('textContent');
if (this.isDuplicate(stanza, original_stanza)) {
return;
}
const jid = stanza.getAttribute('from'),
resource = Strophe.getResourceFromJid(jid),
sender = resource && Strophe.unescapeNode(resource) || '';
if (!this.handleMessageCorrection(stanza)) {
const subject = _.propertyOf(stanza.querySelector('subject'))('textContent');
if (subject) {
u.safeSave(this, {'subject': {'author': sender, 'text': subject}});
}
@ -864,6 +866,7 @@
}
this.incrementUnreadMsgCounter(original_stanza);
this.createMessage(stanza, original_stanza);
}
if (sender !== this.get('nick')) {
// We only emit an event if it's not our own message
_converse.emit('message', {'stanza': original_stanza, 'chatbox': this});
@ -1040,6 +1043,7 @@
_.each(_.difference(old_jids, jids), (removed_jid) => {
// Remove absent occupants who've been removed from
// the members lists.
if (removed_jid === _converse.bare_jid) { return; }
const occupant = this.findOccupant({'jid': removed_jid});
if (!occupant) { return; }
if (occupant.get('show') === 'offline') {

View File

@ -33,7 +33,7 @@
*
* NB: These plugins need to have already been loaded via require.js.
*/
dependencies: ["converse-controlbox", "converse-muc", "converse-bookmarks"],
dependencies: ["converse-singleton", "converse-controlbox", "converse-muc", "converse-bookmarks"],
initialize () {
/* The initialize function gets called as soon as the plugin is
@ -58,6 +58,7 @@
this.browserStorage = new Backbone.BrowserStorage[_converse.storage](
b64_sha1(`converse.open-rooms-{_converse.bare_jid}`));
_converse.chatboxes.on('add', this.onChatBoxAdded, this);
_converse.chatboxes.on('change:hidden', this.onChatBoxChanged, this);
_converse.chatboxes.on('change:bookmarked', this.onChatBoxChanged, this);
_converse.chatboxes.on('change:name', this.onChatBoxChanged, this);
_converse.chatboxes.on('change:num_unread', this.onChatBoxChanged, this);
@ -98,13 +99,14 @@
_converse.RoomsListElementView = Backbone.VDOMView.extend({
events: {
'click a.room-info': 'showRoomDetailsModal'
'click .room-info': 'showRoomDetailsModal'
},
initialize () {
this.model.on('destroy', this.remove, this);
this.model.on('remove', this.remove, this);
this.model.on('change:bookmarked', this.render, this);
this.model.on('change:hidden', this.render, this);
this.model.on('change:name', this.render, this);
this.model.on('change:num_unread', this.render, this);
this.model.on('change:num_unread_general', this.render, this);
@ -118,12 +120,13 @@
// supported by the XMPP server. So we can use it
// as a check for support (other ways of checking are async).
'allow_bookmarks': _converse.allow_bookmarks && _converse.bookmarks,
'info_leave_room': __('Leave this room'),
'info_remove_bookmark': __('Unbookmark this room'),
'info_add_bookmark': __('Bookmark this room'),
'info_title': __('Show more information on this room'),
'currently_open': _converse.isSingleton() && !this.model.get('hidden'),
'info_leave_room': __('Leave this groupchat'),
'info_remove_bookmark': __('Unbookmark this groupchat'),
'info_add_bookmark': __('Bookmark this groupchat'),
'info_title': __('Show more information on this groupchat'),
'name': this.getRoomsListElementName(),
'open_title': __('Click to open this room')
'open_title': __('Click to open this groupchat')
}));
},
@ -153,7 +156,7 @@
events: {
'click .add-bookmark': 'addBookmark',
'click .close-room': 'closeRoom',
'click .rooms-toggle': 'toggleRoomsList',
'click .list-toggle': 'toggleRoomsList',
'click .remove-bookmark': 'removeBookmark',
'click .open-room': 'openRoom',
},
@ -181,8 +184,8 @@
render () {
this.el.innerHTML = tpl_rooms_list({
'toggle_state': this.list_model.get('toggle-state'),
'desc_rooms': __('Click to toggle the rooms list'),
'label_rooms': __('Open Rooms'),
'desc_rooms': __('Click to toggle the list of open groupchats'),
'label_rooms': __('Open Groupchats'),
'_converse': _converse
});
if (this.list_model.get('toggle-state') !== _converse.OPENED) {
@ -225,7 +228,7 @@
ev.preventDefault();
const name = ev.target.getAttribute('data-room-name');
const jid = ev.target.getAttribute('data-room-jid');
if (confirm(__("Are you sure you want to leave the room %1$s?", name))) {
if (confirm(__("Are you sure you want to leave the groupchat %1$s?", name))) {
// TODO: replace with API call
_converse.chatboxviews.get(jid).close();
}

View File

@ -232,10 +232,20 @@
'user_id': Strophe.getNodeFromJid(jid)
}, attributes));
this.setChatBox();
this.presence.on('change:show', () => _converse.emit('contactPresenceChanged', this));
this.presence.on('change:show', () => this.trigger('presenceChanged'));
},
setChatBox (chatbox=null) {
chatbox = chatbox || _converse.chatboxes.get(this.get('jid'));
if (chatbox) {
this.chatbox = chatbox;
this.chatbox.on('change:hidden', this.render, this);
}
},
getDisplayName () {
return this.vcard.get('fullname') || this.get('jid');
},
@ -450,7 +460,7 @@
* (String) name - The name of that user
* (Array of Strings) groups - Any roster groups the user might belong to
* (Function) callback - A function to call once the IQ is returned
* (Function) errback - A function to call if an error occured
* (Function) errback - A function to call if an error occurred
*/
name = _.isEmpty(name)? jid: name;
const iq = $iq({type: 'set'})
@ -798,6 +808,16 @@
/********** Event Handlers *************/
function updateUnreadCounter (chatbox) {
const contact = _converse.roster.findWhere({'jid': chatbox.get('jid')});
if (!_.isUndefined(contact)) {
contact.save({'num_unread': chatbox.get('num_unread')});
}
}
_converse.api.listen.on('chatBoxesInitialized', () => {
_converse.chatboxes.on('change:num_unread', updateUnreadCounter)
});
_converse.api.listen.on('beforeTearDown', _converse.unregisterPresenceHandler());
_converse.api.listen.on('afterTearDown', () => {

View File

@ -96,7 +96,6 @@
'xa': __('This contact is away for an extended period'),
'away': __('This contact is away')
};
const LABEL_CONTACTS = __('Contacts');
const LABEL_GROUPS = __('Groups');
const HEADER_CURRENT_CONTACTS = __('My contacts');
const HEADER_PENDING_CONTACTS = __('Pending contacts');
@ -358,7 +357,7 @@
_converse.RosterContactView = Backbone.NativeView.extend({
tagName: 'li',
className: 'd-flex hidden controlbox-padded',
className: 'list-item d-flex hidden controlbox-padded',
events: {
"click .accept-xmpp-request": "acceptRequest",
@ -369,6 +368,7 @@
initialize () {
this.model.on("change", this.render, this);
this.model.on("highlight", this.highlight, this);
this.model.on("destroy", this.remove, this);
this.model.on("open", this.openChat, this);
this.model.on("remove", this.remove, this);
@ -383,11 +383,10 @@
u.hideElement(this.el);
return this;
}
const item = this.model,
ask = item.get('ask'),
show = item.presence.get('show'),
requesting = item.get('requesting'),
subscription = item.get('subscription');
const ask = this.model.get('ask'),
show = this.model.presence.get('show'),
requesting = this.model.get('requesting'),
subscription = this.model.get('subscription');
const classes_to_remove = [
'current-xmpp-contact',
@ -403,6 +402,18 @@
});
this.el.classList.add(show);
this.el.setAttribute('data-status', show);
this.highlight();
if (_converse.isSingleton()) {
const chatbox = _converse.chatboxes.get(this.model.get('jid'));
if (chatbox) {
if (chatbox.get('hidden')) {
this.el.classList.remove('open');
} else {
this.el.classList.add('open');
}
}
}
if ((ask === 'subscribe') || (subscription === 'from')) {
/* ask === 'subscribe'
@ -416,20 +427,20 @@
*
* So in both cases the user is a "pending" contact.
*/
const display_name = item.getDisplayName();
const display_name = this.model.getDisplayName();
this.el.classList.add('pending-xmpp-contact');
this.el.innerHTML = tpl_pending_contact(
_.extend(item.toJSON(), {
_.extend(this.model.toJSON(), {
'display_name': display_name,
'desc_remove': __('Click to remove %1$s as a contact', display_name),
'allow_chat_pending_contacts': _converse.allow_chat_pending_contacts
})
);
} else if (requesting === true) {
const display_name = item.getDisplayName();
const display_name = this.model.getDisplayName();
this.el.classList.add('requesting-xmpp-contact');
this.el.innerHTML = tpl_requesting_contact(
_.extend(item.toJSON(), {
_.extend(this.model.toJSON(), {
'display_name': display_name,
'desc_accept': __("Click to accept the contact request from %1$s", display_name),
'desc_decline': __("Click to decline the contact request from %1$s", display_name),
@ -440,11 +451,26 @@
this.el.classList.add('current-xmpp-contact');
this.el.classList.remove(_.without(['both', 'to'], subscription)[0]);
this.el.classList.add(subscription);
this.renderRosterItem(item);
this.renderRosterItem(this.model);
}
return this;
},
highlight () {
/* If appropriate, highlight the contact (by adding the 'open' class).
*/
if (_converse.isSingleton()) {
const chatbox = _converse.chatboxes.get(this.model.get('jid'));
if (chatbox) {
if (chatbox.get('hidden')) {
this.el.classList.remove('open');
} else {
this.el.classList.add('open');
}
}
}
},
renderRosterItem (item) {
let status_icon = 'fa-times-circle';
const show = item.presence.get('show') || 'offline';
@ -880,15 +906,14 @@
},
updateChatBox (contact) {
const chatbox = _converse.chatboxes.get(contact.get('jid')),
changes = {};
if (!chatbox) {
if (!this.model.chatbox) {
return this;
}
const changes = {};
if (_.has(contact.changed, 'status')) {
changes.status = contact.get('status');
}
chatbox.save(changes);
this.model.chatbox.save(changes);
return this;
},
@ -937,21 +962,23 @@
/* -------- Event Handlers ----------- */
function updateUnreadCounter (chatbox) {
const contact = _.head(_converse.roster.where({'jid': chatbox.get('jid')}));
if (!_.isUndefined(contact)) {
contact.save({'num_unread': chatbox.get('num_unread')});
}
}
_converse.api.listen.on('chatBoxesInitialized', () => {
_converse.chatboxes.on('change:num_unread', updateUnreadCounter)
_converse.chatboxes.on('change:hidden', (chatbox) => {
const contact = _converse.roster.findWhere({'jid': chatbox.get('jid')});
if (!_.isUndefined(contact)) {
contact.trigger('highlight', contact);
}
});
});
function initRoster () {
/* Create an instance of RosterView once the RosterGroups
* collection has been created (in converse-core.js)
*/
if (_converse.authentication === _converse.ANONYMOUS) {
return;
}
_converse.rosterview = new _converse.RosterView({
'model': _converse.rostergroups
});

View File

@ -50,7 +50,7 @@
if (chatbox.get('id') === 'controlbox') {
return true;
}
if (_.includes(['mobile', 'fullscreen', 'embedded'], _converse.view_mode)) {
if (_converse.isSingleton()) {
const any_chats_visible = _converse.chatboxes
.filter((cb) => cb.get('id') != 'controlbox')
.filter((cb) => !cb.get('hidden')).length > 0;
@ -67,7 +67,8 @@
createChatBox (jid, attrs) {
/* Make sure new chat boxes are hidden by default. */
if (_.includes(['mobile', 'fullscreen', 'embedded'], this.__super__._converse.view_mode)) {
const { _converse } = this.__super__;
if (_converse.isSingleton()) {
attrs = attrs || {};
attrs.hidden = true;
}
@ -77,7 +78,8 @@
ChatBoxView: {
shouldShowOnTextMessage () {
if (_.includes(['mobile', 'fullscreen', 'embedded'], this.__super__._converse.view_mode)) {
const { _converse } = this.__super__;
if (_converse.isSingleton()) {
return false;
} else {
return this.__super__.shouldShowOnTextMessage.apply(this, arguments);
@ -89,7 +91,8 @@
* time. So before opening a chat, we make sure all other
* chats are hidden.
*/
if (_.includes(['mobile', 'fullscreen', 'embedded'], this.__super__._converse.view_mode)) {
const { _converse } = this.__super__;
if (_converse.isSingleton()) {
_.each(this.__super__._converse.chatboxviews.xget(this.model.get('id')), hideChat);
this.model.set('hidden', false);
}
@ -99,7 +102,8 @@
ChatRoomView: {
show (focus) {
if (_.includes(['mobile', 'fullscreen', 'embedded'], this.__super__._converse.view_mode)) {
const { _converse } = this.__super__;
if (_converse.isSingleton()) {
_.each(this.__super__._converse.chatboxviews.xget(this.model.get('id')), hideChat);
this.model.set('hidden', false);
}

View File

@ -1,6 +0,0 @@
<div class="message chat-msg chat-action {{{o.extra_classes}}}" data-isodate="{{{o.time}}}" data-from="{{{o.from}}}">
<span class="chat-msg-heading">
<span class="chat-msg-author">**{{{o.username}}}</span>
</span>
<p class="chat-msg-text"><!-- message gets added here via renderMessage --></p>
</div>

View File

@ -1,8 +1,6 @@
<div class="list-item controlbox-padded room-item available-chatroom d-flex flex-row {[ if (o.hidden) { ]} hidden {[ } ]}" data-room-jid="{{{o.jid}}}">
<a class="open-room w-100" data-room-jid="{{{o.jid}}}" title="{{{o.open_title}}}" href="#">{{{o.name}}}</a>
<a class="remove-bookmark fa fa-bookmark align-self-center {[ if (o.bookmarked) { ]} button-on {[ } ]}"
<a class="list-item-link open-room w-100" data-room-jid="{{{o.jid}}}" title="{{{o.open_title}}}" href="#">{{{o.name}}}</a>
<a class="list-item-action remove-bookmark fa fa-bookmark align-self-center {[ if (o.bookmarked) { ]} button-on {[ } ]}"
data-room-jid="{{{o.jid}}}" data-bookmark-name="{{{o.name}}}"
title="{{{o.info_remove_bookmark}}}" href="#">&nbsp;</a>
<a class="room-info fa fa-info-circle align-self-center" data-room-jid="{{{o.jid}}}"
title="{{{o.info_title}}}" href="#">&nbsp;</a>
</div>

View File

@ -1,4 +1,4 @@
<a href="#" class="rooms-toggle bookmarks-toggle controlbox-padded" title="{{{o.desc_bookmarks}}}">
<a href="#" class="list-toggle bookmarks-toggle controlbox-padded" title="{{{o.desc_bookmarks}}}">
<span class="fa {[ if (o.toggle_state === o._converse.OPENED) { ]} fa-caret-down {[ } else { ]} fa-caret-right {[ } ]}">
</span> {{{o.label_bookmarks}}}</a>
<div class="items-list bookmarks rooms-list {[ if (o.toggle_state !== o._converse.OPENED) { ]} hidden {[ } ]}"></div>

View File

@ -1,4 +1,6 @@
<div class="flyout box-flyout">
<div class="chat-head chat-head-chatroom row no-gutters"></div>
<div class="chat-body chatroom-body row no-gutters"></div>
<div class="chat-body chatroom-body row no-gutters">
<div class="disconnect-container hidden"></div>
</div>
</div>

View File

@ -1,15 +1,19 @@
<div class="modal fade" id="room-details-modal" tabindex="-1" role="dialog" aria-labelledby="user-profile-modal-label" aria-hidden="true">
<div class="modal fade" id="room-details-modal" tabindex="-1" role="dialog" aria-labelledby="room-details-modal-label" aria-hidden="true">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="user-profile-modal-label">{{{o.display_name}}}</h5>
<h5 class="modal-title" id="room-details-modal-label">{{{o.display_name}}}</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="{{{o.label_close}}}"><span aria-hidden="true">&times;</span></button>
</div>
<div class="modal-body">
<div class="room-info">
<p class="room-info"><strong>{{{o.__('Room address (JID)')}}}</strong>: {{{o.jid}}}</p>
<p class="room-info"><strong>{{{o.__('Name')}}}</strong>: {{{o.name}}}</p>
<p class="room-info"><strong>{{{o.__('Room address (JID)')}}}</strong>: {{{o.jid}}}</p>
<p class="room-info"><strong>{{{o.__('Description')}}}</strong>: {{{o.description}}}</p>
{[ if (o.subject) { ]}
<p class="room-info"><strong>{{{o.__('Topic')}}}</strong>: {{o.topic}}</p> <!-- Sanitized in converse-muc-views. We want to render links. -->
<p class="room-info"><strong>{{{o.__('Topic author')}}}</strong>: {{{o._.get(o.subject, 'author')}}}</p>
{[ } ]}
<p class="room-info"><strong>{{{o.__('Online users')}}}</strong>: {{{o.num_occupants}}}</p>
<p class="room-info"><strong>{{{o.__('Features')}}}</strong>:
<div class="chatroom-features">
@ -58,6 +62,9 @@
</p>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">{{{o.__('Close')}}}</button>
</div>
</div>
</div>
</div>

View File

@ -1 +1,7 @@
<p class="disconnect-msg">{{{o.disconnect_message}}}</p>
<div class="alert alert-danger">
<h3 class="alert-heading disconnect-msg">{{{o.disconnect_messages[0]}}}</h3>
{[ o._.forEach(o.disconnect_messages.slice(1), function (msg) { ]}
<p class="disconnect-msg">{{{msg}}}</p>
{[ }); ]}
</div>

View File

@ -3,40 +3,40 @@
{[ } ]}
<ul class="features-list">
{[ if (o.passwordprotected) { ]}
<li class="feature" title="{{{ o.__('This room requires a password before entry') }}}"><span class="fa fa-lock"></span>{{{ o.__('Password protected') }}}</li>
<li class="feature" title="{{{ o.__('This groupchat requires a password before entry') }}}"><span class="fa fa-lock"></span>{{{ o.__('Password protected') }}}</li>
{[ } ]}
{[ if (o.unsecured) { ]}
<li class="feature" title="{{{ o.__('This room does not require a password upon entry') }}}"><span class="fa fa-unlock"></span>{{{ o.__('No password') }}}</li>
<li class="feature" title="{{{ o.__('This groupchat does not require a password upon entry') }}}"><span class="fa fa-unlock"></span>{{{ o.__('No password') }}}</li>
{[ } ]}
{[ if (o.hidden) { ]}
<li class="feature" title="{{{ o.__('This room is not publicly searchable') }}}"><span class="fa fa-eye-slash"></span>{{{ o.__('Hidden') }}}</li>
<li class="feature" title="{{{ o.__('This groupchat is not publicly searchable') }}}"><span class="fa fa-eye-slash"></span>{{{ o.__('Hidden') }}}</li>
{[ } ]}
{[ if (o.public_room) { ]}
<li class="feature" title="{{{ o.__('This room is publicly searchable') }}}"><span class="fa fa-eye"></span>{{{ o.__('Public') }}}</li>
<li class="feature" title="{{{ o.__('This groupchat is publicly searchable') }}}"><span class="fa fa-eye"></span>{{{ o.__('Public') }}}</li>
{[ } ]}
{[ if (o.membersonly) { ]}
<li class="feature" title="{{{ o.__('this room is restricted to members only') }}}"><span class="fa fa-address-book"></span>{{{ o.__('Members only') }}}</li>
<li class="feature" title="{{{ o.__('this groupchat is restricted to members only') }}}"><span class="fa fa-address-book"></span>{{{ o.__('Members only') }}}</li>
{[ } ]}
{[ if (o.open) { ]}
<li class="feature" title="{{{ o.__('Anyone can join this room') }}}"><span class="fa fa-globe"></span>{{{ o.__('Open') }}}</li>
<li class="feature" title="{{{ o.__('Anyone can join this groupchat') }}}"><span class="fa fa-globe"></span>{{{ o.__('Open') }}}</li>
{[ } ]}
{[ if (o.persistent) { ]}
<li class="feature" title="{{{ o.__('This room persists even if it\'s unoccupied') }}}"><span class="fa fa-save"></span>{{{ o.__('Persistent') }}}</li>
<li class="feature" title="{{{ o.__('This groupchat persists even if it\'s unoccupied') }}}"><span class="fa fa-save"></span>{{{ o.__('Persistent') }}}</li>
{[ } ]}
{[ if (o.temporary) { ]}
<li class="feature" title="{{{ o.__('This room will disappear once the last person leaves') }}}"><span class="fa fa-snowflake-o"></span>{{{ o.__('Temporary') }}}</li>
<li class="feature" title="{{{ o.__('This groupchat will disappear once the last person leaves') }}}"><span class="fa fa-snowflake-o"></span>{{{ o.__('Temporary') }}}</li>
{[ } ]}
{[ if (o.nonanonymous) { ]}
<li class="feature" title="{{{ o.__('All other room occupants can see your XMPP username') }}}"><span class="fa fa-id-card"></span>{{{ o.__('Not anonymous') }}}</li>
<li class="feature" title="{{{ o.__('All other groupchat participants can see your XMPP username') }}}"><span class="fa fa-id-card"></span>{{{ o.__('Not anonymous') }}}</li>
{[ } ]}
{[ if (o.semianonymous) { ]}
<li class="feature" title="{{{ o.__('Only moderators can see your XMPP username') }}}"><span class="fa fa-user-secret"></span>{{{ o.__('Semi-anonymous') }}}</li>
{[ } ]}
{[ if (o.moderated) { ]}
<li class="feature" title="{{{ o.__('This room is being moderated') }}}"><span class="fa fa-gavel"></span>{{{ o.__('Moderated') }}}</li>
<li class="feature" title="{{{ o.__('This groupchat is being moderated') }}}"><span class="fa fa-gavel"></span>{{{ o.__('Moderated') }}}</li>
{[ } ]}
{[ if (o.unmoderated) { ]}
<li class="feature" title="{{{ o.__('This room is not being moderated') }}}"><span class="fa fa-info-circle"></span>{{{ o.__('Not moderated') }}}</li>
<li class="feature" title="{{{ o.__('This groupchat is not being moderated') }}}"><span class="fa fa-info-circle"></span>{{{ o.__('Not moderated') }}}</li>
{[ } ]}
{[ if (o.mam_enabled) { ]}
<li class="feature" title="{{{ o.__('Messages are archived on the server') }}}"><span class="fa fa-database"></span>{{{ o.__('Message archiving') }}}</li>

View File

@ -1,7 +1,7 @@
<div class="message chat-msg" data-isodate="{{{o.time}}}" data-msgid="{{{o.msgid}}}">
<canvas class="avatar" height="36" width="36"></canvas>
<div class="chat-msg-content">
<span class="chat-msg-text">Uploading file: <strong>{{{o.file.name}}}</strong>, {{{o.filesize}}}</span>
<canvas class="avatar chat-msg__avatar" height="36" width="36"></canvas>
<div class="chat-msg__content">
<span class="chat-msg__text">Uploading file: <strong>{{{o.file.name}}}</strong>, {{{o.filesize}}}</span>
<progress value="{{{o.progress}}}"/>
</div>
</div>

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