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-bracket-spacing": "off",
"array-callback-return": "error", "array-callback-return": "error",
"arrow-body-style": "off", "arrow-body-style": "off",
"arrow-parens": "error", "arrow-parens": "off",
"arrow-spacing": "error", "arrow-spacing": "error",
"block-scoped-var": "off", "block-scoped-var": "off",
"block-spacing": "off", "block-spacing": "off",

View File

@ -7,6 +7,7 @@
- #161 XEP-0363: HTTP File Upload - #161 XEP-0363: HTTP File Upload
- #194 Include entity capabilities in outgoing presence stanzas - #194 Include entity capabilities in outgoing presence stanzas
- #337 API call to update a VCard - #337 API call to update a VCard
- #421 XEP-0308: Last Message Correction
- #968 Use nickname from VCard when joining a room - #968 Use nickname from VCard when joining a room
- #1091 There's now only one CSS file for all view modes. - #1091 There's now only one CSS file for all view modes.
- #1094 Show room members who aren't currently online - #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) [![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) [![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 It enables you to add chat functionality to your website, independent of
any specific backend. You will however need an XMPP server to connect 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 ### 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 ### 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 ## 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/) - 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 - Single-user and group chats
- Contacts and groups - Contacts and groups
- Multi-user chat rooms [XEP 45](http://xmpp.org/extensions/xep-0045.html) - Multi-user chat rooms [XEP 45](https://xmpp.org/extensions/xep-0045.html)
- Direct invitations to chat rooms [XEP 249](http://xmpp.org/extensions/xep-0249.html) - Direct invitations to chat rooms [XEP 249](https://xmpp.org/extensions/xep-0249.html)
- vCard support [XEP 54](http://xmpp.org/extensions/xep-0054.html) - vCard support [XEP 54](https://xmpp.org/extensions/xep-0054.html)
- Service discovery [XEP 30](http://xmpp.org/extensions/xep-0030.html) - Service discovery [XEP 30](https://xmpp.org/extensions/xep-0030.html)
- In-band registration [XEP 77](http://xmpp.org/extensions/xep-0077.html) - In-band registration [XEP 77](https://xmpp.org/extensions/xep-0077.html)
- Chat room bookmarks [XEP 48](http://xmpp.org/extensions/xep-0048.html) - Chat room bookmarks [XEP 48](https://xmpp.org/extensions/xep-0048.html)
- Roster item exchange [XEP 144](http://xmpp.org/extensions/tmp/xep-0144-1.1.html) - Roster item exchange [XEP 144](https://xmpp.org/extensions/tmp/xep-0144-1.1.html)
- Chat statuses (online, busy, away, offline) - Chat statuses (online, busy, away, offline)
- Custom status messages - Custom status messages
- Desktop notifications - Desktop notifications
- Typing and state notifications [XEP 85](http://xmpp.org/extensions/xep-0085.html) - Typing and state notifications [XEP 85](https://xmpp.org/extensions/xep-0085.html)
- Messages appear in all connnected chat clients [XEP 280](http://xmpp.org/extensions/xep-0280.html) - Messages appear in all connnected chat clients [XEP 280](https://xmpp.org/extensions/xep-0280.html)
- Third person "/me" messages [XEP 245](http://xmpp.org/extensions/xep-0245.html) - Third person "/me" messages [XEP 245](https://xmpp.org/extensions/xep-0245.html)
- XMPP Ping [XEP 199](http://xmpp.org/extensions/xep-0199.html) - XMPP Ping [XEP 199](https://xmpp.org/extensions/xep-0199.html)
- Server-side archiving of messages [XEP 313](http://xmpp.org/extensions/xep-0313.html) - Server-side archiving of messages [XEP 313](https://xmpp.org/extensions/xep-0313.html)
- Hidden Messages (aka Spoilers) [XEP 382](http://xmpp.org/extensions/xep-0382.html) - Hidden Messages (aka Spoilers) [XEP 382](https://xmpp.org/extensions/xep-0382.html)
- Client state indication [XEP 352](http://xmpp.org/extensions/xep-0352.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 - Off-the-record encryption
- Translated into 16 languages - Translated into 16 languages
## Integration into other frameworks ## Integration into other frameworks
- **[Ruby on Rails](http://rubyonrails.org)**: [conversejs-rails](https://github.com/mikemarsian/conversejs-rails) - **[Ruby on Rails](https://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) - **[Django](https://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) - **[Plone](https://plone.com)**: [collective.converse](https://github.com/collective/collective.converse)
- **[Roundcube](http://roundcube.net)**: [roundcube-converse.js-xmpp-plugin](https://github.com/devurandom/roundcube-converse.js-xmpp-plugin) - **[Roundcube](https://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) - **[Wordpress](https://wordpress.org)**: [ConverseJS](https://wordpress.org/plugins/conversejs/)
- **[Patternslib](http://patternslib.com)**: [patterns.converse](https://github.com/jcbrand/patterns.converse) - **[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) - **[Alfresco](https://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) - **[Friendica](https://friendi.ca)**: [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) - **[Tiki Wiki CMS Groupware](https://tiki.org)**: [built-in optional feature](https://doc.tiki.org/XMPP)
## Screencasts ## Screencasts
*Note: These screencasts are already quite old! Converse.js has grown and evolved further since then.* *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. 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. via collective.xmpp.chat.
- [Off-the-record encryption](https://opkode.com/media/blog/2013/11/11/conversejs-otr-support) - [Off-the-record encryption](https://opkode.com/media/blog/2013/11/11/conversejs-otr-support)
in Converse 0.7. in Converse 0.7.
@ -123,4 +124,4 @@ The following people are making recurring donations:
Additionally this project is supported by Additionally this project is supported by
* [![KeyCDN](https://conversejs.org/logo/keycdn.png)](https://www.keycdn.com/) * [![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; } flex-direction: row-reverse; }
#conversejs.converse-fullscreen .converse-chatboxes, #conversejs.converse-mobile .converse-chatboxes { #conversejs.converse-fullscreen .converse-chatboxes, #conversejs.converse-mobile .converse-chatboxes {
width: 100vw; width: 100vw;
right: 15px; } left: -15px; }
#conversejs.converse-overlayed { #conversejs.converse-overlayed {
height: 3em; } height: 3em; }
#conversejs .brand-heading { #conversejs .brand-heading {
@ -7346,9 +7346,8 @@ body.reset {
max-width: 25%; max-width: 25%;
padding: 0; } padding: 0; }
#conversejs .chat-head .user-custom-message { #conversejs .chat-head .user-custom-message {
color: white; color: #e7f7ee;
font-size: 75%; font-size: 75%;
font-style: italic;
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
white-space: nowrap; white-space: nowrap;
@ -7389,7 +7388,7 @@ body.reset {
background-color: #3AA569; background-color: #3AA569;
box-shadow: 1px 3px 5px 3px rgba(0, 0, 0, 0.4); box-shadow: 1px 3px 5px 3px rgba(0, 0, 0, 0.4);
z-index: 1; z-index: 1;
overflow-y: scroll; overflow-y: hidden;
width: 100%; } width: 100%; }
@media screen and (max-height: 450px) { @media screen and (max-height: 450px) {
#conversejs .chatbox .box-flyout { #conversejs .chatbox .box-flyout {
@ -7656,18 +7655,6 @@ body.reset {
#conversejs.converse-overlayed .chatbox .box-flyout { #conversejs.converse-overlayed .chatbox .box-flyout {
min-width: 250px !important; min-width: 250px !important;
width: 250px; } 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-embedded .chatbox form.sendXMPPMessage .chat-toolbar li .toolbar-menu,
#conversejs.converse-overlayed .chatbox form.sendXMPPMessage .chat-toolbar li .toolbar-menu { #conversejs.converse-overlayed .chatbox form.sendXMPPMessage .chat-toolbar li .toolbar-menu {
min-width: 235px; } min-width: 235px; }
@ -7709,7 +7696,7 @@ body.reset {
font-size: 20px; font-size: 20px;
padding: 0; } padding: 0; }
#conversejs.converse-fullscreen .chat-head .user-custom-message { #conversejs.converse-fullscreen .chat-head .user-custom-message {
font-size: 50%; font-size: 70%;
height: auto; height: auto;
line-height: 16px; } line-height: 16px; }
#conversejs.converse-fullscreen .chat-head .chatbox-title { #conversejs.converse-fullscreen .chat-head .chatbox-title {
@ -7733,8 +7720,8 @@ body.reset {
padding-left: 15px; } padding-left: 15px; }
@media (min-width: 768px) { @media (min-width: 768px) {
#conversejs.converse-fullscreen .chatbox { #conversejs.converse-fullscreen .chatbox {
flex: 0 0 75%; flex: 0 0 66.6666666667%;
max-width: 75%; } } max-width: 66.6666666667%; } }
@media (min-width: 992px) { @media (min-width: 992px) {
#conversejs.converse-fullscreen .chatbox { #conversejs.converse-fullscreen .chatbox {
flex: 0 0 75%; flex: 0 0 75%;
@ -7748,21 +7735,12 @@ body.reset {
box-shadow: none; box-shadow: none;
height: 100vh; height: 100vh;
min-height: 50vh; min-height: 50vh;
width: 100%; } width: 100%;
overflow: hidden; }
#conversejs.converse-fullscreen .chatbox .chat-body { #conversejs.converse-fullscreen .chatbox .chat-body {
background-color: #3AA569; background-color: #3AA569;
border-top-left-radius: 4px; border-top-left-radius: 4px;
border-top-right-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 { #conversejs.converse-fullscreen .chatbox .chat-content {
border-top-left-radius: 4px; border-top-left-radius: 4px;
border-top-right-radius: 4px; } border-top-right-radius: 4px; }
@ -7828,15 +7806,24 @@ body.reset {
color: #578EA9; color: #578EA9;
font-size: 20px; font-size: 20px;
margin-right: 0.5em; } 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; } 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; } 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; } color: #E7A151; }
#conversejs .set-xmpp-status .fa-circle-o, #conversejs .xmpp-status .fa-circle-o, #conversejs .roster-contacts .fa-circle-o { #conversejs .set-xmpp-status .fa-circle-o,
color: #A8ABA1; } #conversejs .set-xmpp-status .fa-times-circle,
#conversejs .set-xmpp-status .fa-times-circle, #conversejs .xmpp-status .fa-times-circle, #conversejs .roster-contacts .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; } color: #A8ABA1; }
#conversejs .room-info { #conversejs .room-info {
font-size: 12px; font-size: 12px;
@ -8049,37 +8036,6 @@ body.reset {
#conversejs .toggle-controlbox span { #conversejs .toggle-controlbox span {
color: white; } 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 { #conversejs.converse-overlayed #controlbox {
order: -1; order: -1;
min-width: 250px !important; min-width: 250px !important;
@ -8127,8 +8083,8 @@ body.reset {
@media (min-width: 768px) { @media (min-width: 768px) {
#conversejs.converse-fullscreen #controlbox, #conversejs.converse-fullscreen #controlbox,
#conversejs.converse-mobile #controlbox { #conversejs.converse-mobile #controlbox {
flex: 0 0 25%; flex: 0 0 33.3333333333%;
max-width: 25%; } } max-width: 33.3333333333%; } }
@media (min-width: 992px) { @media (min-width: 992px) {
#conversejs.converse-fullscreen #controlbox, #conversejs.converse-fullscreen #controlbox,
#conversejs.converse-mobile #controlbox { #conversejs.converse-mobile #controlbox {
@ -8256,106 +8212,39 @@ body.reset {
#conversejs.converse-mobile #controlbox #converse-login input[type=button] { #conversejs.converse-mobile #controlbox #converse-login input[type=button] {
width: auto; } width: auto; }
#conversejs .list-container { @media (max-width: 767.98px) {
text-align: left; #conversejs:not(.converse-embedded) {
padding: 0.3em 0; } left: 0;
#conversejs .list-container .rooms-toggle { right: 0;
font-family: "Century Gothic", futura, "URW Gothic L", Verdana, sans-serif; padding-left: env(safe-area-inset-left);
display: block; padding-right: env(safe-area-inset-right); }
color: #666; #conversejs:not(.converse-embedded) .converse-chatboxes {
padding: 0 0 0.5rem 0; } margin: 0 !important;
#conversejs .list-container .rooms-toggle:hover { flex-direction: row !important;
color: #585B51; } justify-content: space-between; }
#conversejs .list-container .items-list { #conversejs:not(.converse-embedded) .converse-chatboxes .converse-chatroom {
text-align: left; } font-size: 14px; }
#conversejs .list-container .items-list .list-item { #conversejs:not(.converse-embedded) .converse-chatboxes .chatbox .box-flyout {
border: none; margin-left: 15px;
clear: both; left: 0;
color: #666; bottom: 0;
display: block; border-radius: 0;
height: 2em; width: 100vw !important;
overflow: hidden; height: 100vh !important; }
padding-top: 0.5em; #conversejs:not(.converse-embedded) .converse-chatboxes #controlbox {
text-shadow: 0 1px 0 #FAFAFA; width: 100vw !important; }
word-wrap: break-word; } #conversejs:not(.converse-embedded) .converse-chatboxes #controlbox .box-flyout {
#conversejs .list-container .items-list .available-chatroom:hover, width: 100vw !important;
#conversejs .list-container .items-list .open-headline:hover, height: 100vh !important; }
#conversejs .list-container .items-list .open-chatroom:hover { #conversejs:not(.converse-embedded) .converse-chatboxes #controlbox .sidebar {
background-color: #eff4f7; } display: block; }
#conversejs .list-container .items-list .available-chatroom:hover a.add-bookmark, #conversejs:not(.converse-embedded) .converse-chatboxes.sidebar-open .chatbox:not(#controlbox) {
#conversejs .list-container .items-list .available-chatroom:hover a.room-info, display: none; }
#conversejs .list-container .items-list .open-headline:hover a.add-bookmark, #conversejs:not(.converse-embedded) .converse-chatboxes.sidebar-open #controlbox .controlbox-pane {
#conversejs .list-container .items-list .open-headline:hover a.room-info, display: block; }
#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 {
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.converse-overlayed .converse-chatboxes .chatbox .box-flyout {
margin-left: 30px; } }
#conversejs #converse-roster { #conversejs #converse-roster {
text-align: left; text-align: left;
width: 100%; width: 100%;
@ -8405,87 +8294,142 @@ body.reset {
padding-bottom: 0.3rem; } padding-bottom: 0.3rem; }
#conversejs #converse-roster .roster-contacts .roster-group .group-toggle:hover { #conversejs #converse-roster .roster-contacts .roster-group .group-toggle:hover {
color: #585B51; } color: #585B51; }
#conversejs #converse-roster .roster-contacts .roster-group li { #conversejs #converse-roster .roster-contacts .roster-group li.requesting-xmpp-contact a {
border: none; line-height: 16px; }
clear: both; #conversejs #converse-roster .roster-contacts .roster-group li.requesting-xmpp-contact a.fa {
color: #666; width: 1.5em; }
display: block; #conversejs #converse-roster .roster-contacts .roster-group li.requesting-xmpp-contact .req-contact-name {
overflow-y: hidden; padding: 0 0.2em 0 0; }
text-shadow: 0 1px 0 #FAFAFA; #conversejs #converse-roster .roster-contacts .roster-group li .open-chat {
line-height: 14px; margin: 0;
width: 100%; padding: 0; }
height: 2em; #conversejs #converse-roster .roster-contacts .roster-group li .open-chat.unread-msgs {
padding-top: 0.5em; } font-weight: bold; }
#conversejs #converse-roster .roster-contacts .roster-group li.requesting-xmpp-contact a { #conversejs #converse-roster .roster-contacts .roster-group li .open-chat.unread-msgs .contact-name {
line-height: 16px; } width: 70%; }
#conversejs #converse-roster .roster-contacts .roster-group li.requesting-xmpp-contact a.fa { #conversejs #converse-roster .roster-contacts .roster-group li .open-chat .msgs-indicator {
width: 1.5em; } color: white;
#conversejs #converse-roster .roster-contacts .roster-group li.requesting-xmpp-contact .req-contact-name { background-color: #3AA569;
padding: 0 0.2em 0 0; } opacity: 1;
#conversejs #converse-roster .roster-contacts .roster-group li a:hover { border-radius: 10%;
color: #206485; } padding: 0.2em;
#conversejs #converse-roster .roster-contacts .roster-group li a .fa:hover { font-size: 12px; }
color: white; } #conversejs #converse-roster .roster-contacts .roster-group li .open-chat .contact-name {
#conversejs #converse-roster .roster-contacts .roster-group li .open-chat {
margin: 0;
padding: 0; }
#conversejs #converse-roster .roster-contacts .roster-group li .open-chat.unread-msgs {
font-weight: bold; }
#conversejs #converse-roster .roster-contacts .roster-group li .open-chat.unread-msgs .contact-name {
width: 70%; }
#conversejs #converse-roster .roster-contacts .roster-group li .open-chat .msgs-indicator {
color: white;
background-color: #3AA569;
opacity: 1;
border-radius: 10%;
padding: 0.2em;
font-size: 12px; }
#conversejs #converse-roster .roster-contacts .roster-group li .open-chat .contact-name {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
padding: 0;
margin: 0;
max-width: 80%;
float: none;
height: 100%; }
#conversejs #converse-roster .roster-contacts .roster-group li .open-chat .contact-name.unread-msgs {
max-width: 60%; }
#conversejs #converse-roster .roster-contacts .roster-group li .open-chat .avatar {
float: left;
display: inline-block; }
#conversejs #converse-roster .roster-contacts .roster-group li.current-xmpp-contact span {
font-size: 14px;
float: left;
margin-right: 0.5em; }
#conversejs #converse-roster .roster-contacts .roster-group li.odd {
background-color: #DCEAC5;
/* Make this difference */ }
#conversejs #converse-roster .roster-contacts .roster-group li a, #conversejs #converse-roster .roster-contacts .roster-group li span {
display: inline-block;
overflow: hidden; overflow: hidden;
white-space: nowrap; white-space: nowrap;
text-overflow: ellipsis; } 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; padding: 0;
width: 2em; margin: 0;
display: none; } max-width: 90%;
#conversejs #converse-roster .roster-contacts .roster-group li .remove-xmpp-contact:before { float: none;
font-size: 14px; } height: 100%; }
#conversejs #converse-roster .roster-contacts .roster-group li:hover { #conversejs #converse-roster .roster-contacts .roster-group li .open-chat .contact-name.unread-msgs {
background-color: #eff4f7; } max-width: 60%; }
#conversejs #converse-roster .roster-contacts .roster-group li:hover .remove-xmpp-contact { #conversejs #converse-roster .roster-contacts .roster-group li .open-chat .avatar {
display: inline-block; } float: left;
display: inline-block; }
#conversejs #converse-roster .roster-contacts .roster-group li.current-xmpp-contact span {
font-size: 14px;
float: left;
margin-right: 0.5em; }
#conversejs #converse-roster .roster-contacts .roster-group li.odd {
background-color: #DCEAC5;
/* Make this difference */ }
#conversejs #converse-roster .roster-contacts .roster-group li a, #conversejs #converse-roster .roster-contacts .roster-group li span {
display: inline-block;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis; }
#conversejs #converse-roster .roster-contacts .roster-group li .decline-xmpp-request {
margin-left: 5px; }
#conversejs #converse-roster .roster-contacts .roster-group li:hover {
background-color: #eff4f7; }
#conversejs #converse-roster .roster-contacts .roster-group li:hover .remove-xmpp-contact {
display: inline-block; }
#conversejs #converse-roster span.pending-contact-name { #conversejs #converse-roster span.pending-contact-name {
line-height: 16px; line-height: 16px;
width: 100%; } 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="submit"],
#conversejs.converse-embedded .add-chatroom input[type="button"], #conversejs.converse-embedded .add-chatroom input[type="button"],
#conversejs .add-chatroom input[type="submit"], #conversejs .add-chatroom input[type="submit"],
@ -8597,9 +8541,13 @@ body.reset {
#conversejs.converse-embedded .chatroom .box-flyout .chatroom-body .mentioned, #conversejs.converse-embedded .chatroom .box-flyout .chatroom-body .mentioned,
#conversejs .chatroom .box-flyout .chatroom-body .mentioned { #conversejs .chatroom .box-flyout .chatroom-body .mentioned {
font-weight: bold; } font-weight: bold; }
#conversejs.converse-embedded .chatroom .box-flyout .chatroom-body .disconnect-msg, #conversejs.converse-embedded .chatroom .box-flyout .chatroom-body .disconnect-container,
#conversejs .chatroom .box-flyout .chatroom-body .disconnect-msg { #conversejs .chatroom .box-flyout .chatroom-body .disconnect-container {
padding: 2em 2em 0 2em; } 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.converse-embedded .chatroom .box-flyout .chatroom-body .chat-area,
#conversejs .chatroom .box-flyout .chatroom-body .chat-area { #conversejs .chatroom .box-flyout .chatroom-body .chat-area {
display: flex; display: flex;
@ -8715,11 +8663,6 @@ body.reset {
#conversejs .chatroom .box-flyout .chatroom-body .chatroom-form-container .validation-message { #conversejs .chatroom .box-flyout .chatroom-body .chatroom-form-container .validation-message {
font-size: 90%; font-size: 90%;
color: #A53214; } 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=button],
#conversejs.converse-embedded .chatroom .box-flyout .chatroom-body .chatroom-form-container input[type=submit], #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], #conversejs .chatroom .box-flyout .chatroom-body .chatroom-form-container input[type=button],
@ -8889,7 +8832,7 @@ body.reset {
width: auto; width: auto;
max-height: 15em; max-height: 15em;
max-width: 100%; } max-width: 100%; }
#conversejs .message.chat-action { #conversejs .message.chat-msg--action {
font-style: italic; } font-style: italic; }
#conversejs .message.chat-msg { #conversejs .message.chat-msg {
display: flex; display: flex;
@ -8901,6 +8844,12 @@ body.reset {
-webkit-animation: colorchange-chatmessage 1s; } -webkit-animation: colorchange-chatmessage 1s; }
#conversejs .message.chat-msg:hover { #conversejs .message.chat-msg:hover {
background-color: rgba(0, 0, 0, 0.035); } 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 { #conversejs .message.chat-msg .spoiler {
margin-top: 0.5em; } margin-top: 0.5em; }
#conversejs .message.chat-msg .spoiler-hint { #conversejs .message.chat-msg .spoiler-hint {
@ -8913,54 +8862,94 @@ body.reset {
#conversejs .message.chat-msg .spoiler-toggle:before { #conversejs .message.chat-msg .spoiler-toggle:before {
padding-right: 0.25em; padding-right: 0.25em;
whitespace: nowrap; } 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; margin-left: 0.5rem;
width: 100%; } width: 100%; }
#conversejs .message.chat-msg.headline .chat-msg-content { #conversejs .message.chat-msg .chat-msg__content--action {
margin-left: 0; } 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; padding: 0;
color: #555; } color: #555;
#conversejs .message.chat-msg .chat-msg-text a { width: 100%; }
#conversejs .message.chat-msg .chat-msg__text a {
word-wrap: break-word; word-wrap: break-word;
word-break: break-all; } word-break: break-all; }
#conversejs .message.chat-msg .chat-msg-text .emojione { #conversejs .message.chat-msg .chat-msg__text .emojione {
margin-bottom: -6px; } margin-bottom: -6px; }
#conversejs .message.chat-msg .chat-msg-media { #conversejs .message.chat-msg .chat-msg__media {
margin-top: 0.25rem; } margin-top: 0.25rem;
#conversejs .message.chat-msg .chat-msg-media a { word-break: break-all; }
#conversejs .message.chat-msg .chat-msg__media a {
word-wrap: break-word; } word-wrap: break-word; }
#conversejs .message.chat-msg .chat-msg-media audio { #conversejs .message.chat-msg .chat-msg__media audio {
width: 100%; } 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; margin-top: 0.5em;
height: 36px; height: 36px;
vertical-align: middle; vertical-align: middle;
width: 36px; } width: 36px; }
#conversejs .message.chat-msg .chat-msg-heading { #conversejs .message.chat-msg .chat-msg__heading {
width: 100%;
margin-top: 0.5em; margin-top: 0.5em;
padding-right: 0.25rem; padding-right: 0.25rem;
padding-bottom: 0.25rem; padding-bottom: 0.25rem;
display: block; } 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-family: "Century Gothic", futura, "URW Gothic L", Verdana, sans-serif;
font-size: 115%; } 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-size: 80%;
font-family: "Helvetica", "Arial", sans-serif; } 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; padding-left: 0.25em;
color: #8c8c8c; } color: #8c8c8c; }
#conversejs .message.chat-msg.chat-action { #conversejs .message.chat-msg.chat-msg--action .chat-msg__content {
display: block; } flex-wrap: wrap;
#conversejs .message.chat-msg.chat-action .chat-msg-heading { flex-direction: row;
float: left; justify-content: flex-start; }
margin-top: 0; #conversejs .message.chat-msg.chat-msg--action .chat-msg__text {
padding-bottom: 0; } width: auto; }
#conversejs .message.chat-msg.chat-msg-followup .chat-msg-heading, #conversejs .message.chat-msg.chat-msg--action .chat-msg__heading {
#conversejs .message.chat-msg.chat-msg-followup .avatar { margin-top: 0;
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; } 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; } margin-left: 2.75rem; }
#conversejs .chatroom-body .message.onload { #conversejs .chatroom-body .message.onload {
animation: colorchange-chatmessage-muc 1s; animation: colorchange-chatmessage-muc 1s;
@ -8968,11 +8957,11 @@ body.reset {
#conversejs .chatroom-body .message .separator { #conversejs .chatroom-body .message .separator {
border: 0.5px solid #E77051; } 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; } margin-left: 0; }
@media screen and (max-width: 767px) { @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; } } white-space: normal; } }
#conversejs.converse-overlayed #minimized-chats { #conversejs.converse-overlayed #minimized-chats {
order: 100; order: 100;
@ -9040,6 +9029,9 @@ body.reset {
#conversejs.converse-overlayed #minimized-chats .chat-head-message-count-hidden { #conversejs.converse-overlayed #minimized-chats .chat-head-message-count-hidden {
display: none; } display: none; }
#conversejs.fullscreen #controlbox #chatrooms .bookmarks-list dl.rooms-list.bookmarks dd.available-chatroom a.open-room {
width: 80%; }
#conversejs [hidden] { #conversejs [hidden] {
display: none; } display: none; }
#conversejs .visually-hidden { #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> <noscript><p><img src="//stats.opkode.com/piwik.php?idsite=1" style="border:0;" alt="" /></p></noscript>
<![if gte IE 11]> <![if gte IE 11]>
<link type="text/css" rel="stylesheet" media="screen" href="/css/converse.css" /> <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]> <![endif]>
<style> <style>

File diff suppressed because it is too large Load Diff

1126
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>Take a look at the <a href="/demo">demo page</a> for other examples of how Converse can be configured and used.</a>
<p> <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. either from a <a href="https://xmpp.net/directory.php">public provider</a>, or one you have set up yourself.
</p> </p>
<h3>Don't have an XMPP/Jabber account?</h3> <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://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://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="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://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/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="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/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/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> <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>
<li><a href="https://conversejs.org/docs/html/plugin_development.html">Plugin Architecture</a></li> <li><a href="https://conversejs.org/docs/html/plugin_development.html">Plugin Architecture</a></li>
<li>Single-user and group chat</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>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="http://xmpp.org/extensions/xep-0048.html" target="_blank" rel="noopener">XEP 48</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="http://xmpp.org/extensions/xep-0249.html" target="_blank" rel="noopener">XEP 249</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="http://xmpp.org/extensions/xep-0054.html" target="_blank" rel="noopener">XEP 54</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="http://xmpp.org/extensions/xep-0030.html" target="_blank" rel="noopener">XEP 30</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="http://xmpp.org/extensions/xep-0077.html" target="_blank" rel="noopener">XEP 77</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="http://xmpp.org/extensions/xep-0144.html" target="_blank" rel="noopener">XEP 144</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>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>Desktop notifications</li>
<li>File sharing (<a href="http://xmpp.org/extensions/xep-0363.html" target="_blank" rel="noopener">XEP 363</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="http://xmpp.org/extensions/xep-0280.html" target="_blank" rel="noopener">XEP 280</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="http://xmpp.org/extensions/xep-0245.html" target="_blank" rel="noopener">XEP 245</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="http://xmpp.org/extensions/xep-0199.html" target="_blank" rel="noopener">XEP 199</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="http://xmpp.org/extensions/xep-0313.html" target="_blank" rel="noopener">XEP 313</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="http://xmpp.org/extensions/xep-0382.html" target="_blank" rel="noopener">XEP 382</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="http://xmpp.org/extensions/xep-0352.html" target="_blank" rel="noopener">XEP 352</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>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>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> <li>Translated into 17 languages</li>
@ -189,11 +190,11 @@
<div class="col-lg-8 col-lg-offset-2"> <div class="col-lg-8 col-lg-offset-2">
<h2>Contact</h2> <h2>Contact</h2>
<ul class="contact"> <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> 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>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>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" class="xmpp JSnocheck" title="Converse chat room">discuss@conference.conversejs.org</a>.</li> <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> <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> </ul>
</div> </div>
@ -205,7 +206,7 @@
However, please don't contact me personally for free support, use However, please don't contact me personally for free support, use
the other channels mentioned above.</br></br> 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> </p>
</div> </div>
</div> </div>
@ -229,7 +230,7 @@
Sponsorships allow us to fund further development and improvements. 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>, 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="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> </p>
</div> </div>
</div> </div>
@ -249,7 +250,7 @@
</p> </p>
<p> <p>
If you're interested in professional XMPP hosting under your 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> </p>
<div class="privacy-policy"> <div class="privacy-policy">
@ -303,7 +304,7 @@
</p> </p>
<p> <p>
If you'd like to have a copy of your data for 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> </p>
<h4>Account deletion</h4> <h4>Account deletion</h4>
<p> <p>
@ -314,7 +315,7 @@
<a href="https://xmpp.org/extensions/xep-0077.html" target="_blank" rel="noopener">XEP-0077</a>. <a href="https://xmpp.org/extensions/xep-0077.html" target="_blank" rel="noopener">XEP-0077</a>.
</p> </p>
<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. and we'll delete your account manually.
</p> </p>
</div> </div>
@ -350,7 +351,7 @@
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE. OTHER DEALINGS IN THE SOFTWARE.
For more information, please refer to <http://unlicense.org/> For more information, please refer to <https://unlicense.org/>
@licend @licend
*/ */
converse.initialize({ 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" "Project-Id-Version: Converse.js 3.3.4\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2018-05-17 11:19+0200\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" "Last-Translator: ButterflyOfFire <ButterflyOfFire@protonmail.com>\n"
"Language-Team: Arabic <https://hosted.weblate.org/projects/conversejs/" "Language-Team: Arabic <https://hosted.weblate.org/projects/conversejs/"
"translations/ar/>\n" "translations/ar/>\n"
@ -18,7 +18,7 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 " "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" "&& 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 #: dist/converse-no-dependencies.js:9853 dist/converse-no-dependencies.js:9882
msgid "Download" msgid "Download"
@ -153,7 +153,7 @@ msgstr ""
#: dist/converse-no-dependencies.js:21257 #: dist/converse-no-dependencies.js:21257
msgid "Sorry, could not succesfully upload your file." msgid "Sorry, could not succesfully upload your file."
msgstr "" msgstr "للأسف لم نتمكّن مِن القيام برفع ملفك بنجاح."
#: dist/converse-no-dependencies.js:21260 #: dist/converse-no-dependencies.js:21260
#, javascript-format #, javascript-format
@ -162,7 +162,7 @@ msgstr "رد الخادم : \"%1$s\""
#: dist/converse-no-dependencies.js:21442 #: dist/converse-no-dependencies.js:21442
msgid "Sorry, looks like file upload is not supported by your server." msgid "Sorry, looks like file upload is not supported by your server."
msgstr "" msgstr "للأسف يبدو أن خاصية رفع الملفات لا يدعمها خادومكم."
#: dist/converse-no-dependencies.js:21452 #: dist/converse-no-dependencies.js:21452
#, javascript-format #, javascript-format
@ -171,8 +171,7 @@ msgid ""
"which is %2$s." "which is %2$s."
msgstr "" msgstr ""
#: dist/converse-no-dependencies.js:22197 #: dist/converse-no-dependencies.js:18681
#, fuzzy
msgid "Show more" msgid "Show more"
msgstr "عرض المزيد" msgstr "عرض المزيد"
@ -330,7 +329,11 @@ msgstr "كتب كأنه شخص ثالث"
msgid "Show this menu" msgid "Show this menu"
msgstr "إظهار هذه القائمة" msgstr "إظهار هذه القائمة"
<<<<<<< HEAD
#: dist/converse-no-dependencies.js:19614
=======
#: dist/converse-no-dependencies.js:23164 #: dist/converse-no-dependencies.js:23164
>>>>>>> master
msgid "Are you sure you want to clear the messages from this conversation?" msgid "Are you sure you want to clear the messages from this conversation?"
msgstr "هل أنت متأكد أنك تود مسح الرسائل مِن نافذة المحادثة هذه ؟" msgstr "هل أنت متأكد أنك تود مسح الرسائل مِن نافذة المحادثة هذه ؟"
@ -344,11 +347,103 @@ msgstr "قد قطع الإتصال"
msgid "is busy" msgid "is busy"
msgstr "مشغول" 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 #: dist/converse-no-dependencies.js:23260
msgid "is online" msgid "is online"
msgstr "متصل" msgstr "متصل"
#: dist/converse-no-dependencies.js:23501 #: dist/converse-no-dependencies.js:23501
>>>>>>> master
msgid "XMPP Username:" msgid "XMPP Username:"
msgstr "إسم المستخدِم :" msgstr "إسم المستخدِم :"
@ -533,6 +628,13 @@ msgstr "إضافة مراسل"
#: dist/converse-no-dependencies.js:25288 #: dist/converse-no-dependencies.js:25288
msgid "Your Profile" msgid "Your Profile"
msgstr "ملفك الشخصي" msgstr "ملفك الشخصي"
<<<<<<< HEAD
#: dist/converse-no-dependencies.js:25346
#: dist/converse-no-dependencies.js:25358
msgid "Close"
msgstr "إغلاق"
=======
#: dist/converse-no-dependencies.js:25293 #: dist/converse-no-dependencies.js:25293
#, fuzzy #, fuzzy
@ -557,6 +659,7 @@ msgstr "المعذرة، لقد طرأ هناك خطأ أثناء محاولة
#: dist/converse-no-dependencies.js:25325 #: dist/converse-no-dependencies.js:25325
msgid "You can check your browser's developer console for any error output." msgid "You can check your browser's developer console for any error output."
msgstr "" msgstr ""
>>>>>>> master
#: dist/converse-no-dependencies.js:25377 #: dist/converse-no-dependencies.js:25377
msgid "Custom status" msgid "Custom status"
@ -657,7 +760,11 @@ msgstr "غرفة المحادثة هذه ليست مجهولة"
msgid "This room now shows unavailable members" msgid "This room now shows unavailable members"
msgstr "" msgstr ""
<<<<<<< HEAD
#: dist/converse-no-dependencies.js:28646
=======
#: dist/converse-no-dependencies.js:28652 #: dist/converse-no-dependencies.js:28652
>>>>>>> master
#, fuzzy #, fuzzy
msgid "This room does not show unavailable members" msgid "This room does not show unavailable members"
msgstr "هذه القاعة لا تقوم بعرض الأعضاء المشغولين" msgstr "هذه القاعة لا تقوم بعرض الأعضاء المشغولين"
@ -827,7 +934,7 @@ msgstr "ليست تحت الإشراف"
#: dist/converse-no-dependencies.js:28777 #: dist/converse-no-dependencies.js:28777
msgid "Query for Chatrooms" msgid "Query for Chatrooms"
msgstr "" msgstr "الإستعلام عن قاعات الدردشة"
#: dist/converse-no-dependencies.js:28778 #: dist/converse-no-dependencies.js:28778
msgid "Server address" msgid "Server address"
@ -869,22 +976,35 @@ msgstr "الإلتحاق بالغرفة"
msgid "Message" msgid "Message"
msgstr "رسالة" msgstr "رسالة"
<<<<<<< HEAD
#: dist/converse-no-dependencies.js:29053
#, javascript-format
=======
#: dist/converse-no-dependencies.js:29058 #: dist/converse-no-dependencies.js:29058
#, fuzzy, javascript-format #, fuzzy, javascript-format
>>>>>>> master
msgid "%1$s is no longer a moderator" msgid "%1$s is no longer a moderator"
msgstr "لم يعُد %1$s مِن مُشْرِفي غرفة المحادثة." msgstr "لم يعُد %1$s مِن مُشْرِفي غرفة المحادثة"
#: dist/converse-no-dependencies.js:29061 #: dist/converse-no-dependencies.js:29061
#, fuzzy, javascript-format #, fuzzy, javascript-format
msgid "%1$s has been given a voice again" msgid "%1$s has been given a voice again"
msgstr "لقد تم طرد %1$s مِن غرفة المحادثة مؤقتًا" msgstr "لقد تم طرد %1$s مِن غرفة المحادثة مؤقتًا"
<<<<<<< HEAD
#: dist/converse-no-dependencies.js:29059
=======
#: dist/converse-no-dependencies.js:29064 #: dist/converse-no-dependencies.js:29064
>>>>>>> master
#, javascript-format #, javascript-format
msgid "%1$s has been muted" msgid "%1$s has been muted"
msgstr "تم كتم %1$s" msgstr "تم كتم %1$s"
<<<<<<< HEAD
#: dist/converse-no-dependencies.js:29062
=======
#: dist/converse-no-dependencies.js:29067 #: dist/converse-no-dependencies.js:29067
>>>>>>> master
#, javascript-format #, javascript-format
msgid "%1$s is now a moderator" msgid "%1$s is now a moderator"
msgstr "أصبح %1$s مُشرفًا" msgstr "أصبح %1$s مُشرفًا"
@ -1081,9 +1201,14 @@ msgstr "غُرف المحادثة"
msgid "Add a new room" msgid "Add a new room"
msgstr "إضافة غرفة جديدة" msgstr "إضافة غرفة جديدة"
<<<<<<< HEAD
#: dist/converse-no-dependencies.js:29953
#, fuzzy
=======
#: dist/converse-no-dependencies.js:29983 #: dist/converse-no-dependencies.js:29983
>>>>>>> master
msgid "Query for rooms" msgid "Query for rooms"
msgstr "" msgstr "البحث عن قاعات"
#: dist/converse-no-dependencies.js:30022 #: dist/converse-no-dependencies.js:30022
#, javascript-format #, javascript-format
@ -1410,6 +1535,8 @@ msgstr "تم التحقق منه"
#: dist/converse-no-dependencies.js:31192 #: dist/converse-no-dependencies.js:31192
msgid "finished" msgid "finished"
msgstr "انتهى" msgstr "انتهى"
<<<<<<< HEAD
=======
#: dist/converse-no-dependencies.js:31788 #: dist/converse-no-dependencies.js:31788
#, javascript-format #, javascript-format
@ -1419,6 +1546,7 @@ msgstr "المعذرة، لقد حدث هناك خطأ أثناء محاولة
#: dist/converse-no-dependencies.js:31936 #: dist/converse-no-dependencies.js:31936
msgid "This client does not allow presence subscriptions" msgid "This client does not allow presence subscriptions"
msgstr "" msgstr ""
>>>>>>> master
#: dist/converse-no-dependencies.js:32028 #: dist/converse-no-dependencies.js:32028
msgid "Click to hide these contacts" 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" "Project-Id-Version: Converse.js 0.4\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2018-05-17 11:19+0200\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" "Last-Translator: Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>\n"
"Language-Team: French <https://hosted.weblate.org/projects/conversejs/" "Language-Team: French <https://hosted.weblate.org/projects/conversejs/"
"translations/fr/>\n" "translations/fr/>\n"
@ -92,50 +92,9 @@ msgstr "Marquer ce salon"
msgid "The name for this bookmark:" msgid "The name for this bookmark:"
msgstr "Nom de ce marque-page:" msgstr "Nom de ce marque-page:"
#: dist/converse-no-dependencies.js:16466 #: dist/converse-no-dependencies.js:17811
msgid "Would you like this room to be automatically joined upon startup?" msgid "Sorry, could not determine file upload URL."
msgstr "Voulez-vous rejoindre automatiquement ce salon au lancement?" msgstr "Désolé, impossible de déterminer lURL pour envoyer le fichier."
#: 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:16741 #: dist/converse-no-dependencies.js:16741
#: dist/converse-no-dependencies.js:28819 #: 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 " "La taille de votre fichier, %1$s, dépasse le maximum autorisé par votre "
"serveur, qui est %2$s." "serveur, qui est %2$s."
#: dist/converse-no-dependencies.js:22197 #: dist/converse-no-dependencies.js:18681
msgid "Show more" msgid "Show more"
msgstr "Afficher plus" 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 #: dist/converse-no-dependencies.js:22488
msgid "Close this chat box" msgid "Close this chat box"
msgstr "Fermer cette fenêtre de discussion" 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" msgid "Clear all messages"
msgstr "Supprimer tous les messages" msgstr "Supprimer tous les messages"
#: dist/converse-no-dependencies.js:22755 #: dist/converse-no-dependencies.js:19149
msgid "Insert emojis" msgid "Insert emojis"
msgstr "Insérer un emoji" msgstr "Insérer un emoji"
@ -347,7 +284,7 @@ msgstr "Écrire à la troisième personne"
msgid "Show this menu" msgid "Show this menu"
msgstr "Afficher ce 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?" msgid "Are you sure you want to clear the messages from this conversation?"
msgstr "Voulez-vous vraiment effacer les messages de cette conversation?" msgstr "Voulez-vous vraiment effacer les messages de cette conversation?"
@ -361,7 +298,7 @@ msgstr "sest déconnecté"
msgid "is busy" msgid "is busy"
msgstr "est occupé" msgstr "est occupé"
#: dist/converse-no-dependencies.js:23260 #: dist/converse-no-dependencies.js:19710
msgid "is online" msgid "is online"
msgstr "est en ligne" msgstr "est en ligne"
@ -891,22 +828,22 @@ msgstr "Rejoindre"
msgid "Message" msgid "Message"
msgstr "Message" msgstr "Message"
#: dist/converse-no-dependencies.js:29058 #: dist/converse-no-dependencies.js:29053
#, javascript-format #, javascript-format
msgid "%1$s is no longer a moderator" msgid "%1$s is no longer a moderator"
msgstr "%1$s nest plus un modérateur" msgstr "%1$s nest plus un modérateur"
#: dist/converse-no-dependencies.js:29061 #: dist/converse-no-dependencies.js:29056
#, javascript-format #, javascript-format
msgid "%1$s has been given a voice again" msgid "%1$s has been given a voice again"
msgstr "%1$s peut de nouveau parler" msgstr "%1$s peut de nouveau parler"
#: dist/converse-no-dependencies.js:29064 #: dist/converse-no-dependencies.js:29059
#, javascript-format #, javascript-format
msgid "%1$s has been muted" msgid "%1$s has been muted"
msgstr "%1$s ne peut plus parler" msgstr "%1$s ne peut plus parler"
#: dist/converse-no-dependencies.js:29067 #: dist/converse-no-dependencies.js:29062
#, javascript-format #, javascript-format
msgid "%1$s is now a moderator" msgid "%1$s is now a moderator"
msgstr "%1$s est désormais un modérateur" 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" "Project-Id-Version: Converse.js 0.4\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2018-05-17 11:19+0200\n" "POT-Creation-Date: 2018-05-17 11:19+0200\n"
"PO-Revision-Date: 2018-04-30 19:54+0000\n" "PO-Revision-Date: 2018-07-03 10:22+0000\n"
"Last-Translator: Nathan Follens <nathan@email.is>\n" "Last-Translator: Nathan Follens <nthn@unseen.is>\n"
"Language-Team: Dutch <https://hosted.weblate.org/projects/conversejs/" "Language-Team: Dutch <https://hosted.weblate.org/projects/conversejs/"
"translations/nl/>\n" "translations/nl/>\n"
"Language: nl\n" "Language: nl\n"
@ -17,7 +17,7 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n != 1;\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" "domain: converse\n"
"lang: nl\n" "lang: nl\n"
"plural_forms: nplurals=2; plural=(n != 1);\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 " "De grootte van het bestand, %1$s, overschrijdt het maximum toegestaan door "
"je server, %2$s." "je server, %2$s."
#: dist/converse-no-dependencies.js:22197 #: dist/converse-no-dependencies.js:18681
msgid "Show more" msgid "Show more"
msgstr "Meer tonen" 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 #: dist/converse-no-dependencies.js:22488
msgid "Close this chat box" msgid "Close this chat box"
msgstr "Dit gespreksvenster sluiten" msgstr "Dit gespreksvenster sluiten"
#: dist/converse-no-dependencies.js:22516 #: dist/converse-no-dependencies.js:22516
msgid "The User's Profile Image" msgid "The User's Profile Image"
msgstr "" msgstr "Profielafbeelding van gebruiker"
#: dist/converse-no-dependencies.js:22519 #: dist/converse-no-dependencies.js:22519
#: dist/converse-no-dependencies.js:25289 #: dist/converse-no-dependencies.js:25289
@ -220,18 +198,16 @@ msgstr "Sluiten"
#: dist/converse-no-dependencies.js:22520 #: dist/converse-no-dependencies.js:22520
#: dist/converse-no-dependencies.js:25290 #: dist/converse-no-dependencies.js:25290
msgid "Email" msgid "Email"
msgstr "" msgstr "E-mail"
#: dist/converse-no-dependencies.js:22521 #: dist/converse-no-dependencies.js:22521
#: dist/converse-no-dependencies.js:25291 #: dist/converse-no-dependencies.js:25291
#, fuzzy
msgid "Full Name" msgid "Full Name"
msgstr "Naam" msgstr "Volledige naam"
#: dist/converse-no-dependencies.js:22522 #: dist/converse-no-dependencies.js:22522
#, fuzzy
msgid "Jabber ID" msgid "Jabber ID"
msgstr "XMPP-ID:" msgstr "Jabber-ID"
#: dist/converse-no-dependencies.js:22523 #: dist/converse-no-dependencies.js:22523
#: dist/converse-no-dependencies.js:25292 #: dist/converse-no-dependencies.js:25292
@ -240,23 +216,22 @@ msgid "Nickname"
msgstr "Bijnaam" msgstr "Bijnaam"
#: dist/converse-no-dependencies.js:22524 #: dist/converse-no-dependencies.js:22524
#, fuzzy
msgid "Remove as contact" msgid "Remove as contact"
msgstr "Voeg een contact toe" msgstr "Contact verwijderen"
#: dist/converse-no-dependencies.js:22525 #: dist/converse-no-dependencies.js:22525
msgid "Refresh" msgid "Refresh"
msgstr "" msgstr "Verversen"
#: dist/converse-no-dependencies.js:22526 #: dist/converse-no-dependencies.js:22526
#: dist/converse-no-dependencies.js:25294 #: dist/converse-no-dependencies.js:25294
msgid "Role" msgid "Role"
msgstr "" msgstr "Rol"
#: dist/converse-no-dependencies.js:22527 #: dist/converse-no-dependencies.js:22527
#: dist/converse-no-dependencies.js:25297 #: dist/converse-no-dependencies.js:25297
msgid "URL" msgid "URL"
msgstr "" msgstr "URL"
#: dist/converse-no-dependencies.js:22566 #: dist/converse-no-dependencies.js:22566
#: dist/converse-no-dependencies.js:24293 #: dist/converse-no-dependencies.js:24293
@ -313,7 +288,7 @@ msgstr "Klik hier om een verborgen bericht te schrijven"
msgid "Clear all messages" msgid "Clear all messages"
msgstr "Alle berichten wissen" msgstr "Alle berichten wissen"
#: dist/converse-no-dependencies.js:22755 #: dist/converse-no-dependencies.js:19149
msgid "Insert emojis" msgid "Insert emojis"
msgstr "Voeg smileys in" msgstr "Voeg smileys in"
@ -335,7 +310,7 @@ msgstr "Schrijf in de derde persoon"
msgid "Show this menu" msgid "Show this menu"
msgstr "Toon dit 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?" 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?" msgstr "Weet je zeker dat je de berichten in dit gesprek wil wissen?"
@ -349,10 +324,96 @@ msgstr "is offline"
msgid "is busy" msgid "is busy"
msgstr "is bezet" msgstr "is bezet"
#: dist/converse-no-dependencies.js:23260 #: dist/converse-no-dependencies.js:19710
msgid "is online" msgid "is online"
msgstr "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 #: dist/converse-no-dependencies.js:23501
msgid "XMPP Username:" msgid "XMPP Username:"
msgstr "XMPP-gebruikersnaam:" msgstr "XMPP-gebruikersnaam:"
@ -540,28 +601,29 @@ msgid "Your Profile"
msgstr "Je profiel" msgstr "Je profiel"
#: dist/converse-no-dependencies.js:25293 #: dist/converse-no-dependencies.js:25293
#, fuzzy
msgid "XMPP Address (JID)" msgid "XMPP Address (JID)"
msgstr "XMPP-adres" msgstr "XMPP-adres (JID)"
#: dist/converse-no-dependencies.js:25295 #: dist/converse-no-dependencies.js:25295
msgid "" msgid ""
"Use commas to separate multiple roles. Your roles are shown next to your " "Use commas to separate multiple roles. Your roles are shown next to your "
"name on your chat messages." "name on your chat messages."
msgstr "" 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 #: dist/converse-no-dependencies.js:25298
msgid "Your avatar image" msgid "Your avatar image"
msgstr "" msgstr "Jouw avatarafbeelding"
#: dist/converse-no-dependencies.js:25325 #: dist/converse-no-dependencies.js:25325
#, fuzzy
msgid "Sorry, an error happened while trying to save your profile data." 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 #: dist/converse-no-dependencies.js:25325
msgid "You can check your browser's developer console for any error output." msgid "You can check your browser's developer console for any error output."
msgstr "" msgstr ""
"Je kan de ontwikkelaarsconsole van je browser nakijken voor foutenuitvoer."
#: dist/converse-no-dependencies.js:25377 #: dist/converse-no-dependencies.js:25377
msgid "Custom status" msgid "Custom status"
@ -878,22 +940,22 @@ msgstr "Deelnemen"
msgid "Message" msgid "Message"
msgstr "Bericht" msgstr "Bericht"
#: dist/converse-no-dependencies.js:29058 #: dist/converse-no-dependencies.js:29053
#, javascript-format #, javascript-format
msgid "%1$s is no longer a moderator" msgid "%1$s is no longer a moderator"
msgstr "%1$s is geen moderator meer" msgstr "%1$s is geen moderator meer"
#: dist/converse-no-dependencies.js:29061 #: dist/converse-no-dependencies.js:29056
#, javascript-format #, javascript-format
msgid "%1$s has been given a voice again" msgid "%1$s has been given a voice again"
msgstr "%1$s heeft terug een stem" msgstr "%1$s heeft terug een stem"
#: dist/converse-no-dependencies.js:29064 #: dist/converse-no-dependencies.js:29059
#, javascript-format #, javascript-format
msgid "%1$s has been muted" msgid "%1$s has been muted"
msgstr "%1$s is gedempt" msgstr "%1$s is gedempt"
#: dist/converse-no-dependencies.js:29067 #: dist/converse-no-dependencies.js:29062
#, javascript-format #, javascript-format
msgid "%1$s is now a moderator" msgid "%1$s is now a moderator"
msgstr "%1$s is nu een 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 " "Sorry, an error happened while running the command. Check your browser's "
"developer console for details." "developer console for details."
msgstr "" 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 #: dist/converse-no-dependencies.js:29263
msgid "Change user's affiliation to admin" 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." msgstr "Deze gebruiker kan GEEN berichten sturen in dit groepsgesprek."
#: dist/converse-no-dependencies.js:30026 #: dist/converse-no-dependencies.js:30026
#, fuzzy
msgid "Moderator" msgid "Moderator"
msgstr "Gemodereerd" msgstr "Moderator"
#: dist/converse-no-dependencies.js:30027 #: dist/converse-no-dependencies.js:30027
msgid "Visitor" msgid "Visitor"
msgstr "" msgstr "Bezoeker"
#: dist/converse-no-dependencies.js:30028 #: dist/converse-no-dependencies.js:30028
msgid "Owner" msgid "Owner"
msgstr "" msgstr "Eigenaar"
#: dist/converse-no-dependencies.js:30029 #: dist/converse-no-dependencies.js:30029
#, fuzzy
msgid "Member" msgid "Member"
msgstr "Alleen-leden" msgstr "Lid"
#: dist/converse-no-dependencies.js:30030 #: dist/converse-no-dependencies.js:30030
msgid "Admin" msgid "Admin"
msgstr "" msgstr "Beheerder"
#: dist/converse-no-dependencies.js:30082 #: dist/converse-no-dependencies.js:30082
msgid "Occupants" 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" "Project-Id-Version: Converse.js 3.3.4\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2018-05-17 11:19+0200\n" "POT-Creation-Date: 2018-05-17 11:19+0200\n"
"PO-Revision-Date: 2018-04-30 19:58+0000\n" "PO-Revision-Date: 2018-07-03 10:26+0000\n"
"Last-Translator: Nathan Follens <nathan@email.is>\n" "Last-Translator: Nathan Follens <nthn@unseen.is>\n"
"Language-Team: Flemish <https://hosted.weblate.org/projects/conversejs/" "Language-Team: Flemish <https://hosted.weblate.org/projects/conversejs/"
"translations/nl_BE/>\n" "translations/nl_BE/>\n"
"Language: nl_BE\n" "Language: nl_BE\n"
@ -17,7 +17,7 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n != 1;\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 #: dist/converse-no-dependencies.js:9853 dist/converse-no-dependencies.js:9882
msgid "Download" msgid "Download"
@ -174,7 +174,7 @@ msgstr ""
"De grootte van het bestand, %1$s, overschrijd het maximum toegelaten door " "De grootte van het bestand, %1$s, overschrijd het maximum toegelaten door "
"uwe server, %2$s." "uwe server, %2$s."
#: dist/converse-no-dependencies.js:22197 #: dist/converse-no-dependencies.js:18681
msgid "Show more" msgid "Show more"
msgstr "Meer tonen" msgstr "Meer tonen"
@ -206,7 +206,7 @@ msgstr "Dit gespreksvenster sluiten"
#: dist/converse-no-dependencies.js:22516 #: dist/converse-no-dependencies.js:22516
msgid "The User's Profile Image" msgid "The User's Profile Image"
msgstr "" msgstr "Profielafbeelding van gebruiker"
#: dist/converse-no-dependencies.js:22519 #: dist/converse-no-dependencies.js:22519
#: dist/converse-no-dependencies.js:25289 #: dist/converse-no-dependencies.js:25289
@ -217,16 +217,16 @@ msgstr "Sluiten"
#: dist/converse-no-dependencies.js:22520 #: dist/converse-no-dependencies.js:22520
#: dist/converse-no-dependencies.js:25290 #: dist/converse-no-dependencies.js:25290
msgid "Email" msgid "Email"
msgstr "" msgstr "E-mail"
#: dist/converse-no-dependencies.js:22521 #: dist/converse-no-dependencies.js:22521
#: dist/converse-no-dependencies.js:25291 #: dist/converse-no-dependencies.js:25291
msgid "Full Name" msgid "Full Name"
msgstr "" msgstr "Volledige naam"
#: dist/converse-no-dependencies.js:22522 #: dist/converse-no-dependencies.js:22522
msgid "Jabber ID" msgid "Jabber ID"
msgstr "" msgstr "Jabber-ID"
#: dist/converse-no-dependencies.js:22523 #: dist/converse-no-dependencies.js:22523
#: dist/converse-no-dependencies.js:25292 #: dist/converse-no-dependencies.js:25292
@ -235,23 +235,22 @@ msgid "Nickname"
msgstr "Bijnaam" msgstr "Bijnaam"
#: dist/converse-no-dependencies.js:22524 #: dist/converse-no-dependencies.js:22524
#, fuzzy
msgid "Remove as contact" msgid "Remove as contact"
msgstr "Voeg een contact toe" msgstr "Contact verwijderen"
#: dist/converse-no-dependencies.js:22525 #: dist/converse-no-dependencies.js:22525
msgid "Refresh" msgid "Refresh"
msgstr "" msgstr "Vernieuwen"
#: dist/converse-no-dependencies.js:22526 #: dist/converse-no-dependencies.js:22526
#: dist/converse-no-dependencies.js:25294 #: dist/converse-no-dependencies.js:25294
msgid "Role" msgid "Role"
msgstr "" msgstr "Rol"
#: dist/converse-no-dependencies.js:22527 #: dist/converse-no-dependencies.js:22527
#: dist/converse-no-dependencies.js:25297 #: dist/converse-no-dependencies.js:25297
msgid "URL" msgid "URL"
msgstr "" msgstr "URL"
#: dist/converse-no-dependencies.js:22566 #: dist/converse-no-dependencies.js:22566
#: dist/converse-no-dependencies.js:24293 #: 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:22575
#: dist/converse-no-dependencies.js:25325 #: dist/converse-no-dependencies.js:25325
msgid "Error" msgid "Error"
msgstr "" msgstr "Fout"
#: dist/converse-no-dependencies.js:22575 #: dist/converse-no-dependencies.js:22575
#: dist/converse-no-dependencies.js:24301 #: dist/converse-no-dependencies.js:24301
@ -308,7 +307,7 @@ msgstr "Klikt hier voor een verborgen bericht te schrijven"
msgid "Clear all messages" msgid "Clear all messages"
msgstr "Alle berichten wissen" msgstr "Alle berichten wissen"
#: dist/converse-no-dependencies.js:22755 #: dist/converse-no-dependencies.js:19149
msgid "Insert emojis" msgid "Insert emojis"
msgstr "Voegd smileys in" msgstr "Voegd smileys in"
@ -330,7 +329,7 @@ msgstr "Schrijfd in den derde persoon"
msgid "Show this menu" msgid "Show this menu"
msgstr "Toond dit 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?" 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?" msgstr "Zij ge zeker da ge de berichten in dit gesprek wild wissen?"
@ -344,10 +343,96 @@ msgstr "is offline"
msgid "is busy" msgid "is busy"
msgstr "is bezet" msgstr "is bezet"
#: dist/converse-no-dependencies.js:23260 #: dist/converse-no-dependencies.js:19710
msgid "is online" msgid "is online"
msgstr "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 #: dist/converse-no-dependencies.js:23501
msgid "XMPP Username:" msgid "XMPP Username:"
msgstr "XMPP-gebruikersnaam:" msgstr "XMPP-gebruikersnaam:"
@ -535,28 +620,30 @@ msgid "Your Profile"
msgstr "Uw profiel" msgstr "Uw profiel"
#: dist/converse-no-dependencies.js:25293 #: dist/converse-no-dependencies.js:25293
#, fuzzy
msgid "XMPP Address (JID)" msgid "XMPP Address (JID)"
msgstr "XMPP-adres" msgstr "XMPP-adres (JID)"
#: dist/converse-no-dependencies.js:25295 #: dist/converse-no-dependencies.js:25295
msgid "" msgid ""
"Use commas to separate multiple roles. Your roles are shown next to your " "Use commas to separate multiple roles. Your roles are shown next to your "
"name on your chat messages." "name on your chat messages."
msgstr "" 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 #: dist/converse-no-dependencies.js:25298
msgid "Your avatar image" msgid "Your avatar image"
msgstr "" msgstr "Uw avatarafbeelding"
#: dist/converse-no-dependencies.js:25325 #: dist/converse-no-dependencies.js:25325
#, fuzzy
msgid "Sorry, an error happened while trying to save your profile data." 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 #: dist/converse-no-dependencies.js:25325
msgid "You can check your browser's developer console for any error output." msgid "You can check your browser's developer console for any error output."
msgstr "" msgstr ""
"Ge kunt de ontwikkelaarsconsole van uwen browser nakijken voor foutenuitvoer."
#: dist/converse-no-dependencies.js:25377 #: dist/converse-no-dependencies.js:25377
msgid "Custom status" msgid "Custom status"
@ -873,22 +960,22 @@ msgstr "Deelnemen"
msgid "Message" msgid "Message"
msgstr "Bericht" msgstr "Bericht"
#: dist/converse-no-dependencies.js:29058 #: dist/converse-no-dependencies.js:29053
#, javascript-format #, javascript-format
msgid "%1$s is no longer a moderator" msgid "%1$s is no longer a moderator"
msgstr "%1$s is gene moderator meer" msgstr "%1$s is gene moderator meer"
#: dist/converse-no-dependencies.js:29061 #: dist/converse-no-dependencies.js:29056
#, javascript-format #, javascript-format
msgid "%1$s has been given a voice again" msgid "%1$s has been given a voice again"
msgstr "%1$s heefd terug een stem" msgstr "%1$s heefd terug een stem"
#: dist/converse-no-dependencies.js:29064 #: dist/converse-no-dependencies.js:29059
#, javascript-format #, javascript-format
msgid "%1$s has been muted" msgid "%1$s has been muted"
msgstr "%1$s is gedempt" msgstr "%1$s is gedempt"
#: dist/converse-no-dependencies.js:29067 #: dist/converse-no-dependencies.js:29062
#, javascript-format #, javascript-format
msgid "%1$s is now a moderator" msgid "%1$s is now a moderator"
msgstr "%1$s is nu ne 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 " "Sorry, an error happened while running the command. Check your browser's "
"developer console for details." "developer console for details."
msgstr "" 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 #: dist/converse-no-dependencies.js:29263
msgid "Change user's affiliation to admin" 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." msgstr "Deze gebruiker kan GEEN berichten sturen in dit groepsgesprek."
#: dist/converse-no-dependencies.js:30026 #: dist/converse-no-dependencies.js:30026
#, fuzzy
msgid "Moderator" msgid "Moderator"
msgstr "Gemodereerd" msgstr "Moderator"
#: dist/converse-no-dependencies.js:30027 #: dist/converse-no-dependencies.js:30027
msgid "Visitor" msgid "Visitor"
msgstr "" msgstr "Bezoeker"
#: dist/converse-no-dependencies.js:30028 #: dist/converse-no-dependencies.js:30028
msgid "Owner" msgid "Owner"
msgstr "" msgstr "Eigenaar"
#: dist/converse-no-dependencies.js:30029 #: dist/converse-no-dependencies.js:30029
#, fuzzy
msgid "Member" msgid "Member"
msgstr "Alleen-leden" msgstr "Lid"
#: dist/converse-no-dependencies.js:30030 #: dist/converse-no-dependencies.js:30030
msgid "Admin" msgid "Admin"
msgstr "" msgstr "Beheerder"
#: dist/converse-no-dependencies.js:30082 #: dist/converse-no-dependencies.js:30082
msgid "Occupants" 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> </head>
<body class="reset"> <body class="reset">
<div id="conversejs" class="fullscreen"> <div id="conversejs" class="fullscreen converse-fullscreen">
<div class="sidebar"></div> <div class="sidebar"></div>
<div class="converse-chatboxes row no-gutters"> <div class="converse-chatboxes row no-gutters">
<div id="controlbox" class="chatbox"> <div id="controlbox" class="chatbox">
@ -42,15 +42,22 @@
<div class="message chat-info chat-error">This is an error message</div> <div class="message chat-info chat-error">This is an error message</div>
<div class="message chat-msg"> <div class="message chat-msg">
<canvas data-avatar="/mockup/images/romeo.jpg" height="36" width="36" class="avatar"></canvas> <canvas data-avatar="/mockup/images/romeo.jpg" height="36" width="36" class="avatar chat-msg__avatar"></canvas>
<div class="chat-msg-content"> <div class="chat-msg__content">
<span class="chat-msg-heading"> <span class="chat-msg__heading">
<span class="chat-msg-author">Romeo Montague</span> <span class="chat-msg__author">Romeo Montague</span>
<span class="chat-msg-time">15:31</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> </span>
<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> </div>
@ -60,59 +67,87 @@
</div> </div>
<div class="message chat-msg"> <div class="message chat-msg">
<canvas data-avatar="/mockup/images/romeo.jpg" height="36" width="36" class="avatar"></canvas> <canvas data-avatar="/mockup/images/romeo.jpg" height="36" width="36" class="avatar chat-msg__avatar"></canvas>
<div class="chat-msg-content"> <div class="chat-msg__content">
<span class="chat-msg-heading"> <span class="chat-msg__heading">
<span class="chat-msg-author">Romeo Montague</span> <span class="chat-msg__author">Romeo Montague</span>
<span class="chat-msg-time">15:31</span> <span class="chat-msg__time">15:31</span>
</span>
<span class="chat-msg-text">
But soft, what light through yonder window breaks?
</span> </span>
<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> </div>
<div class="message chat-msg chat-msg-followup"> <div class="message chat-msg chat-msg-followup">
<canvas data-avatar="/mockup/images/romeo.jpg" height="36" width="36" class="avatar"></canvas> <canvas data-avatar="/mockup/images/romeo.jpg" height="36" width="36" class="avatar chat-msg__avatar"></canvas>
<div class="chat-msg-content"> <div class="chat-msg__content">
<span class="chat-msg-heading"> <span class="chat-msg__heading">
<span class="chat-msg-author">Romeo Montague</span> <span class="chat-msg__author">Romeo Montague</span>
<span class="chat-msg-time">15:31</span> <span class="chat-msg__time">15:31</span>
</span>
<span class="chat-msg-text">
It is the east and Juliet is the sun!
</span> </span>
<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> </div>
<div class="message chat-msg chat-msg-followup"> <div class="message chat-msg chat-msg-followup">
<canvas data-avatar="/mockup/images/romeo.jpg" height="36" width="36" class="avatar"></canvas> <canvas data-avatar="/mockup/images/romeo.jpg" height="36" width="36" class="avatar chat-msg__avatar"></canvas>
<div class="chat-msg-content"> <div class="chat-msg__content">
<span class="chat-msg-heading"> <span class="chat-msg__heading">
<span class="chat-msg-author">Romeo Montague</span> <span class="chat-msg__author">Romeo Montague</span>
<span class="chat-msg-time">15:31</span> <span class="chat-msg__time">15:31</span>
</span>
<span class="chat-msg-text">
Arise, fair sun, and kill the envious moon,
</span> </span>
<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> </div>
<div class="message chat-info chat-event">Romeo Montague is busy</div> <div class="message chat-info chat-event">Romeo Montague is busy</div>
<div class="message chat-msg"> <div class="message chat-msg">
<canvas height="36" width="36" class="avatar"></canvas> <canvas height="36" width="36" class="avatar chat-msg__avatar"></canvas>
<div class="chat-msg-content"> <div class="chat-msg__content">
<span class="chat-msg-heading"> <span class="chat-msg__heading">
<span class="chat-msg-author">Juliet Capulet</span> <span class="chat-msg__author">Juliet Capulet</span>
<span class="chat-msg-time">15:31</span> <span class="chat-msg__time">15:31</span>
</span>
<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,
And I'll no longer be a Capulet.
</span> </span>
<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,
And I'll no longer be a Capulet.
</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> </div>
</div> </div>

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;"> <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> Romeo Montague has entered the room</div>
<div class="message chat-msg chat-action" data-isodate="2018-04-36T18:07:38+02:00"> <div class="message chat-msg chat-msg--action" data-isodate="2018-04-36T18:07:38+02:00">
<span class="chat-msg-heading"> <div class="chat-msg__content chat-msg__content--action">
<span class="chat-msg-author">**Romeo Montague</span> <span class="chat-msg__heading">
</span> <time timestamp="2018-12-29" class="chat-msg__time">15:29</time>
<span class="chat-msg-text">looks around</span> <span class="chat-msg__author">**Romeo Montague</span>
</span>
<span class="chat-msg__text">looks around</span>
</div>
</div> </div>
<div class="message chat-msg"> <div class="message chat-msg">
<canvas data-avatar="/mockup/images/romeo.jpg" height="36" width="36" class="avatar"></canvas> <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__content">
<span class="chat-msg-heading"> <div class="chat-msg__heading">
<span class="chat-msg-author">Romeo Montague <span class="badge badge-primary">Developer</span></span> <span class="chat-msg__author">Romeo Montague <span class="badge badge-primary">Developer</span></span>
<span class="chat-msg-time">15:31</span> <span class="chat-msg__time">15:31</span>
</span> </div>
<span class="chat-msg-text">He jests at scars that never felt a wound.</span> <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>
</div> </div>
@ -71,144 +81,205 @@
Juliet has entered the room</div> Juliet has entered the room</div>
<div class="message chat-msg"> <div class="message chat-msg">
<canvas data-avatar="/mockup/images/romeo.jpg" height="36" width="36" class="avatar"></canvas> <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__content">
<span class="chat-msg-heading"> <div class="chat-msg__heading">
<span class="chat-msg-author">Romeo Montague</span> <span class="chat-msg__author">Romeo Montague</span>
<span class="chat-msg-time">19:36</span> <span class="chat-msg__time">19:36</span>
</span> </div>
<span class="chat-msg-text"> <div class="chat-msg__body">
But, soft! what light through yonder window breaks? <div class="chat-msg__message">
</span> <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> </div>
</div> </div>
<div class="message chat-msg chat-msg-followup"> <div class="message chat-msg chat-msg--followup">
<canvas data-avatar="/mockup/images/romeo.jpg" height="36" width="36" class="avatar"></canvas> <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__content">
<span class="chat-msg-heading"> <div class="chat-msg__heading">
<span class="chat-msg-author">Romeo Montague</span> <span class="chat-msg__author">Romeo Montague</span>
<span class="chat-msg-time">19:36</span> <span class="chat-msg__time">19:36</span>
</span> </div>
<span class="chat-msg-text">It is the east, and Juliet is the sun.</span> <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> </div>
<div class="message chat-msg chat-msg-followup"> <div class="message chat-msg chat-msg--followup">
<canvas data-avatar="/mockup/images/romeo.jpg" height="36" width="36" class="avatar"></canvas> <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__content">
<span class="chat-msg-heading"> <div class="chat-msg__heading">
<span class="chat-msg-author">Romeo Montague</span> <span class="chat-msg__author">Romeo Montague</span>
<span class="chat-msg-time">19:36</span> <span class="chat-msg__time">19:36</span>
</span> </div>
<span class="chat-msg-text">Arise, fair sun, and kill the envious moon, Who is already sick and pale with grief</span> <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> </div>
<div class="message chat-msg"> <div class="message chat-msg">
<canvas height="36" width="36" class="avatar"></canvas> <canvas class="avatar chat-msg__avatar" height="36" width="36"></canvas>
<div class="chat-msg-content"> <div class="chat-msg__content">
<span class="chat-msg-heading"> <div class="chat-msg__heading">
<span class="chat-msg-author">Juliet Capulet</span> <span class="chat-msg__author">Juliet Capulet</span>
<span class="chat-msg-time">19:43</span> <span class="chat-msg__time">19:43</span>
</span> </div>
<span class="chat-msg-text"> <div class="chat-msg__body">
O Romeo, Romeo! wherefore art thou Romeo? <div class="chat-msg__message">
Deny thy father and refuse thy name; <div class="chat-msg__text">
Or, if thou wilt not, be but sworn my love, O Romeo, Romeo! wherefore art thou Romeo?
And I'll no longer be a Capulet. Deny thy father and refuse thy name;
</span> Or, if thou wilt not, be but sworn my love,
<div class="chat-msg-media"></div> And I'll no longer be a Capulet.
</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> </div>
<div class="message chat-msg"> <div class="message chat-msg">
<canvas data-avatar="/mockup/images/romeo.jpg" height="36" width="36" class="avatar"></canvas> <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__content">
<span class="chat-msg-heading"> <div class="chat-msg__heading">
<span class="chat-msg-author">Romeo Montague</span> <span class="chat-msg__author">Romeo Montague</span>
<span class="chat-msg-time">19:36</span> <span class="chat-msg__time">19:36</span>
</span> </div>
<span class="chat-msg-text">Uploading file: <strong>juliet.jpg</strong>, 120kb</span> <div class="chat-msg__body">
<progress value="50" max="100"/> <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> </div>
<div class="message chat-msg"> <div class="message chat-msg">
<canvas height="36" width="36" class="avatar"></canvas> <canvas class="avatar chat-msg__avatar" height="36" width="36"></canvas>
<div class="chat-msg-content"> <div class="chat-msg__content">
<span class="chat-msg-heading"> <div class="chat-msg__heading">
<span class="chat-msg-author">Juliet Capulet</span> <span class="chat-msg__author">Juliet Capulet</span>
<span class="chat-msg-time">19:45</span> <span class="chat-msg__time">19:45</span>
</span> </div>
<span class="chat-msg-text"></span> <div class="chat-msg__body">
<div class="chat-msg-media"> <div class="chat-msg__message">
<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" <div class="chat-msg__media">
target="_blank" rel="noopener"> <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"
<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"> target="_blank" rel="noopener">
</a> <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">
</a>
</div>
</div>
</div> </div>
</div> </div>
</div> </div>
<div class="message chat-msg" data-isodate="2018-04-36T18:07:36+02:00" data-msgid="some-long-id"> <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> <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__content">
<span class="chat-msg-heading"> <span class="chat-msg__heading">
<span class="chat-msg-author">Romeo Montague</span> <span class="chat-msg__author">Romeo Montague</span>
<span class="chat-msg-time">19:36</span> <span class="chat-msg__time">19:36</span>
</span> </span>
<div> <div class="chat-msg__body">
<span class="spoiler-hint">By a name</span> <div class="chat-msg__message">
<a class="badge badge-info spoiler-toggle" data-toggle-state="closed" href="#"><i class="fa fa-eye"></i>Show more</a> <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">
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>
<div class="chat-msg__actions">
<button class="chat-msg__action fa fa-pencil" title="Edit this message">&nbsp;</button>
</div>
</div> </div>
<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>
</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;">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-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 <div class="message chat-info chat-topic" data-isodate="2018-03-07T10:21:09+01:00">
patient if your questions aren't answered immediately. We're all in different timezones.</div> 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"> <div class="message chat-msg">
<canvas height="36" width="36" class="avatar"></canvas> <canvas class="avatar chat-msg__avatar" height="36" width="36"></canvas>
<div class="chat-msg-content"> <div class="chat-msg__content">
<span class="chat-msg-heading"> <div class="chat-msg__heading">
<span class="chat-msg-author">Mercutio</span> <span class="chat-msg__author">Mercutio</span>
<span class="chat-msg-time">19:49</span> <span class="chat-msg__time">19:49</span>
</span> </div>
<span class="chat-msg-text">I mean, sir, in delay We waste our lights in vain, like lamps by day.</span> <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> </div>
<div class="message chat-msg chat-msg-followup"> <div class="message chat-msg chat-msg--followup">
<canvas height="36" width="36" class="avatar"></canvas> <canvas class="avatar chat-msg__avatar" height="36" width="36"></canvas>
<div class="chat-msg-content"> <div class="chat-msg__content">
<span class="chat-msg-heading"> <div class="chat-msg__heading">
<span class="chat-msg-author">Mercutio</span> <span class="chat-msg__author">Mercutio</span>
<span class="chat-msg-time">19:49</span> <span class="chat-msg__time">19:49</span>
</span> </div>
<span class="chat-msg-text"> <div class="chat-msg__body">
Take our good meaning, for our judgment sits. <div class="chat-msg__message">
Five times in that ere once in our five wits.</span> <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> </div>
<div class="message chat-msg chat-msg-followup"> <div class="message chat-msg chat-msg--followup">
<canvas height="36" width="36" class="avatar"></canvas> <canvas class="avatar chat-msg__avatar" height="36" width="36"></canvas>
<div class="chat-msg-content"> <div class="chat-msg__content">
<span class="chat-msg-heading"> <span class="chat-msg__heading">
<span class="chat-msg-author">Mercutio</span> <span class="chat-msg__author">Mercutio</span>
<span class="chat-msg-time">19:49</span> <span class="chat-msg__time">19:49</span>
</span> </span>
<span class="chat-msg-text"> <div class="chat-msg__body">
True, I talk of dreams, Which are the children of an idle brain, Begot of nothing but vain fantasy, <div class="chat-msg__message">
Which is as thin of substance as the air And more inconstant than the wind, who wooes <span class="chat-msg__text">
Even now the frozen bosom of the north, And, being anger'd, puffs away from thence, True, I talk of dreams, Which are the children of an idle brain, Begot of nothing but vain fantasy,
Turning his face to the dew-dropping south.</span> 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>
</div> </div>

View File

@ -35,24 +35,24 @@
<span class="w-100 controlbox-heading">Group Chats</span> <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> <a class="chatbox-btn fa fa-users" title="Click to add a new room" data-toggle="modal" data-target="#chatroomsModal"></a>
</div> </div>
<div class="list-container rooms-list-container"> <div class="list-container items-list-container">
<div class="rooms-list items-list"> <div class="items-list">
<div class="controlbox-padded list-item available-chatroom d-flex flex-row"> <div class="controlbox-padded list-item available-chatroom d-flex flex-row open">
<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> --> <!-- <span class="badge badge-info msgs-indicator">1</span> -->
<a href="#" <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"> 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> &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 class="controlbox-padded list-item available-chatroom d-flex flex-row"> <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="#" <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"> 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> &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> </div>
</div> </div>
@ -62,24 +62,24 @@
<div class="d-flex controlbox-padded"> <div class="d-flex controlbox-padded">
<span class="w-100 controlbox-heading">Bookmarks</span> <span class="w-100 controlbox-heading">Bookmarks</span>
</div> </div>
<div class="list-container rooms-list-container"> <div class="list-container items-list-container">
<div class="rooms-list items-list"> <div class="items-list">
<div class="controlbox-padded list-item available-chatroom d-flex flex-row"> <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> --> <!-- <span class="badge badge-info msgs-indicator">1</span> -->
<a href="#" <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"> 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> &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 class="controlbox-padded list-item available-chatroom d-flex flex-row"> <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="#" <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"> 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> &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> </div>
</div> </div>
@ -108,13 +108,13 @@
<div class="roster-group" id="xmpp-contact-requests"> <div class="roster-group" id="xmpp-contact-requests">
<a href="#" class="group-toggle controlbox-padded " title="Click to hide these contacts"> <a href="#" class="group-toggle controlbox-padded " title="Click to hide these contacts">
<span class="fa fa-caret-down"></span> Contact Requests</a> <span class="fa fa-caret-down"></span> Contact Requests</a>
<ul class="roster-group-contacts"> <ul class="items-list roster-group-contacts">
<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">The Nurse</span> <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="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> <a class="decline-xmpp-request fa fa-times" title="Click here to decline this contact's request" href="#"></a>
</li> </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> <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="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> <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"> <div class="roster-group" data-group="Colleagues">
<a href="#" data-group="Colleagues" class="group-toggle controlbox-padded " title="Click to hide these contacts"> <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> <span class="fa fa-caret-down"></span> Colleagues</a>
<ul> <ul class="items-list roster-group-contacts">
<li class=" controlbox-padded away current-xmpp-contact d-flex"> <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"> <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> <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>
<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"> <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> <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>
</li> </li>
</div> </div>
@ -142,21 +142,21 @@
<div class="roster-group" data-group="Family"> <div class="roster-group" data-group="Family">
<a href="#" data-group="Family" class="group-toggle controlbox-padded " title="Click to hide these contacts"> <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> <span class="fa fa-caret-down"></span> Family</a>
<ul> <ul class="items-list roster-group-contacts">
<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"> <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> <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>
<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"> <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> <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>
<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"> <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> <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> </li>
</ul> </ul>
</div> </div>
@ -164,11 +164,11 @@
<div class="roster-group" data-group="Friends"> <div class="roster-group" data-group="Friends">
<a href="#" data-group="Friends" class="group-toggle controlbox-padded " title="Click to hide these contacts"> <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> <span class="fa fa-caret-down"></span> Friends</a>
<ul> <ul class="items-list roster-group-contacts">
<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"> <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> <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> </li>
</ul> </ul>
</div> </div>
@ -176,21 +176,21 @@
<div class="roster-group" data-group="Ungrouped"> <div class="roster-group" data-group="Ungrouped">
<a href="#" class="group-toggle controlbox-padded " title="Click to hide these contacts"> <a href="#" class="group-toggle controlbox-padded " title="Click to hide these contacts">
<span class="fa fa-caret-down"></span> Ungrouped</a> <span class="fa fa-caret-down"></span> Ungrouped</a>
<ul> <ul class="items-list roster-group-contacts">
<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"> <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> <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>
<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"> <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> <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>
<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"> <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> <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> </li>
</ul> </ul>
</div> </div>
@ -198,14 +198,14 @@
<div class="roster-group" id="pending-xmpp-contacts"> <div class="roster-group" id="pending-xmpp-contacts">
<a href="#" class="group-toggle controlbox-padded " title="Click to hide these contacts"> <a href="#" class="group-toggle controlbox-padded " title="Click to hide these contacts">
<span class="fa fa-caret-down"></span> Pending Contacts</a> <span class="fa fa-caret-down"></span> Pending Contacts</a>
<ul> <ul class="items-list roster-group-contacts">
<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">An Apothecary</span> <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>
<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> <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> </li>
</ul> </ul>
</div> </div>

View File

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

View File

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

View File

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

View File

@ -119,8 +119,12 @@
.mentioned { .mentioned {
font-weight: bold; font-weight: bold;
} }
.disconnect-msg { .disconnect-container {
padding: 2em 2em 0 2em; margin: 1em;
width: 100%;
h3.disconnect-msg {
padding-bottom: 1em;
}
} }
.chat-area { .chat-area {
display: flex; display: flex;
@ -245,12 +249,6 @@
font-size: 90%; font-size: 90%;
color: $error-color; color: $error-color;
} }
.chatroom-form {
label,
input[type=text] {
display: block;
}
}
input[type=button], input[type=button],
input[type=submit] { input[type=submit] {
margin: 0 0.5em; 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 { .fa-circle {
color: $green; color: $green;
} }
@ -30,9 +32,7 @@
.fa-dot-circle-o { .fa-dot-circle-o {
color: $orange, color: $orange,
} }
.fa-circle-o { .fa-circle-o,
color: $subdued-color;
}
.fa-times-circle { .fa-times-circle {
color: $subdued-color; 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 { #conversejs.converse-overlayed {
#controlbox { #controlbox {
@ -461,8 +407,9 @@
#conversejs.converse-mobile { #conversejs.converse-mobile {
#controlbox { #controlbox {
@include make-col-ready(); @include make-col-ready();
@include media-breakpoint-up(md) { @include media-breakpoint-up(md) {
@include make-col(3); @include make-col(4);
} }
@include media-breakpoint-up(lg) { @include media-breakpoint-up(lg) {
@include make-col(3); @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-mobile {
.converse-chatboxes { .converse-chatboxes {
width: 100vw; width: 100vw;
right: 15px; // Hack due to padding added by bootstrap left: -15px; // Hack due to padding added by bootstrap
} }
} }
&.converse-overlayed { &.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-height: 15em;
max-width: 100%; max-width: 100%;
} }
&.chat-action { &.chat-msg--action {
font-style: italic; font-style: italic;
} }
@ -72,6 +72,19 @@
} }
&:hover { &:hover {
background-color: rgba(0, 0, 0, 0.035); 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 { .spoiler {
@ -91,19 +104,46 @@
whitespace: nowrap; whitespace: nowrap;
} }
} }
.chat-msg-content {
.chat-msg__content {
display: flex;
flex-direction: column;
justify-content: space-between;
align-items: stretch;
margin-left: 0.5rem; margin-left: 0.5rem;
width: 100%; 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 { &.headline {
.chat-msg-content { .chat-msg__body {
margin-left: 0; margin-left: 0;
} }
} }
.chat-msg-text { .chat-msg__text {
padding: 0; padding: 0;
color: $message-text-color; color: $message-text-color;
width: 100%;
a { a {
word-wrap: break-word; word-wrap: break-word;
word-break: break-all; word-break: break-all;
@ -113,8 +153,9 @@
} }
} }
.chat-msg-media { .chat-msg__media {
margin-top: 0.25rem; margin-top: 0.25rem;
word-break: break-all;
a { a {
word-wrap: break-word; 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; margin-top: 0.5em;
height: 36px; height: 36px;
vertical-align: middle; vertical-align: middle;
width: 36px; width: 36px;
} }
.chat-msg-heading {
.chat-msg__heading {
width: 100%;
margin-top: 0.5em; margin-top: 0.5em;
padding-right: 0.25rem; padding-right: 0.25rem;
padding-bottom: 0.25rem; padding-bottom: 0.25rem;
display: block; display: block;
.chat-msg-author { .chat-msg__author {
white-space: nowrap;
font-family: $heading-font; font-family: $heading-font;
font-size: 115%; font-size: 115%;
.badge { .badge {
@ -143,26 +202,39 @@
font-family: $normal_font; font-family: $normal_font;
} }
} }
.chat-msg-time { .chat-msg__time {
padding-left: 0.25em; padding-left: 0.25em;
color: lighten($text-color, 15%); color: lighten($text-color, 15%);
} }
} }
&.chat-action { &.chat-msg--action {
display: block; .chat-msg__content {
flex-wrap: wrap;
.chat-msg-heading { flex-direction: row;
float: left; justify-content: flex-start;
}
.chat-msg__text {
width: auto;
}
.chat-msg__heading {
margin-top: 0; margin-top: 0;
padding-bottom: 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, &.chat-msg--followup {
.avatar { .chat-msg__heading,
.chat-msg__avatar {
display: none; display: none;
} }
.chat-msg-content { .chat-msg__content {
margin-left: 2.75rem; margin-left: 2.75rem;
} }
} }
@ -183,8 +255,8 @@
#conversejs.converse-overlayed { #conversejs.converse-overlayed {
.message { .message {
&.chat-msg { &.chat-msg {
&.chat-msg-followup { &.chat-msg--followup {
.chat-msg-content { .chat-msg__body {
margin-left: 0; margin-left: 0;
} }
} }
@ -196,7 +268,7 @@
#conversejs:not(.converse-embedded) { #conversejs:not(.converse-embedded) {
.message { .message {
&.chat-msg { &.chat-msg {
.chat-msg-author { .chat-msg__author {
white-space: normal; 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 { 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 { &.requesting-xmpp-contact {
a { a {
line-height: $line-height; line-height: $line-height;
@ -94,14 +83,6 @@
padding: 0 0.2em 0 0; padding: 0 0.2em 0 0;
} }
} }
a {
&:hover {
color: $dark-link-color;
}
.fa:hover {
color: white;
}
}
.open-chat { .open-chat {
margin: 0; margin: 0;
@ -128,7 +109,7 @@
text-overflow: ellipsis; text-overflow: ellipsis;
padding: 0; padding: 0;
margin: 0; margin: 0;
max-width: 80%; max-width: 90%;
float: none; float: none;
height: 100%; height: 100%;
&.unread-msgs { &.unread-msgs {
@ -156,22 +137,9 @@
white-space: nowrap; white-space: nowrap;
text-overflow: ellipsis; text-overflow: ellipsis;
} }
span {
padding: 0;
}
.decline-xmpp-request { .decline-xmpp-request {
margin-left: 5px; 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 { &:hover {
background-color: lighten($controlbox-head-color, 45%); background-color: lighten($controlbox-head-color, 45%);
.remove-xmpp-contact { .remove-xmpp-contact {

View File

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

View File

@ -518,20 +518,28 @@
_converse.connection._dataRecv(test_utils.createRequest(stanza)); _converse.connection._dataRecv(test_utils.createRequest(stanza));
test_utils.waitUntil(function () { 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 () { }, 300).then(function () {
expect($('#chatrooms div.bookmarks.rooms-list .room-item').length).toBe(4); expect(document.querySelectorAll('#chatrooms div.bookmarks.rooms-list .room-item').length).toBe(4);
expect($('#chatrooms div.bookmarks.rooms-list .room-item a').text().trim()).toBe( const els = document.querySelectorAll('#chatrooms div.bookmarks.rooms-list .room-item a.list-item-link');
"1st Bookmark  Another room  Bookmark with a very very long name that will be shortened  The Play's the Thing") 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); 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(); expect(window.confirm).toHaveBeenCalled();
return test_utils.waitUntil(function () { return test_utils.waitUntil(function () {
return $('#chatrooms .bookmarks.rooms-list .room-item a').text().trim() === return document.querySelectorAll('#chatrooms div.bookmarks.rooms-list .room-item').length === 3;
"1st Bookmark  Bookmark with a very very long name that will be shortened  The Play's the Thing";
}, 300) }, 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 () { test_utils.waitUntil(function () {
return u.isVisible(view.el); return u.isVisible(view.el);
}).then(function () { }).then(function () {
expect(view.el.querySelectorAll('.chat-action').length).toBe(1); expect(view.el.querySelectorAll('.chat-msg--action').length).toBe(1);
expect(_.includes(view.el.querySelector('.chat-msg-author').textContent, '**Max Frankfurter')).toBeTruthy(); 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).find('.chat-msg__text').text()).toBe(' is tired');
message = '/me is as well'; message = '/me is as well';
test_utils.sendMessage(view, message); 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 () { }).then(function () {
expect($(view.el).find('.chat-msg-text:last').text()).toBe(' is as well'); 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:last').hasClass('chat-msg--followup')).toBe(false);
// Check that /me messages after a normal message don't // 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'; message = 'This a normal message';
test_utils.sendMessage(view, message); test_utils.sendMessage(view, message);
let message_el = view.el.querySelector('.message:last-child'); 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'; message = '/me wrote a 3rd person message';
test_utils.sendMessage(view, message); test_utils.sendMessage(view, message);
message_el = view.el.querySelector('.message:last-child'); message_el = view.el.querySelector('.message:last-child');
expect(view.el.querySelectorAll('.chat-action').length).toBe(3); 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__text:last').text()).toBe(' wrote a 3rd person message');
expect($(view.el).find('.chat-msg-author:last').is(':visible')).toBeTruthy(); expect($(view.el).find('.chat-msg__author:last').is(':visible')).toBeTruthy();
expect(u.hasClass('chat-msg-followup', message_el)).toBeFalsy(); expect(u.hasClass('chat-msg--followup', message_el)).toBeFalsy();
done(); done();
}); });
}); });
@ -635,7 +635,7 @@
spyOn(_converse.connection, 'send'); spyOn(_converse.connection, 'send');
spyOn(_converse, 'emit'); spyOn(_converse, 'emit');
view.keyPressed({ view.keyPressed({
target: $(view.el).find('textarea.chat-textarea'), target: view.el.querySelector('textarea.chat-textarea'),
keyCode: 1 keyCode: 1
}); });
expect(view.model.get('chat_state')).toBe('composing'); expect(view.model.get('chat_state')).toBe('composing');
@ -648,7 +648,7 @@
// The notification is not sent again // The notification is not sent again
view.keyPressed({ view.keyPressed({
target: $(view.el).find('textarea.chat-textarea'), target: view.el.querySelector('textarea.chat-textarea'),
keyCode: 1 keyCode: 1
}); });
expect(view.model.get('chat_state')).toBe('composing'); expect(view.model.get('chat_state')).toBe('composing');
@ -776,7 +776,7 @@
spyOn(view, 'setChatState').and.callThrough(); spyOn(view, 'setChatState').and.callThrough();
expect(view.model.get('chat_state')).toBe('active'); expect(view.model.get('chat_state')).toBe('active');
view.keyPressed({ view.keyPressed({
target: $(view.el).find('textarea.chat-textarea'), target: view.el.querySelector('textarea.chat-textarea'),
keyCode: 1 keyCode: 1
}); });
expect(view.model.get('chat_state')).toBe('composing'); expect(view.model.get('chat_state')).toBe('composing');
@ -803,14 +803,14 @@
// out if the user simply types longer than the // out if the user simply types longer than the
// timeout. // timeout.
view.keyPressed({ view.keyPressed({
target: $(view.el).find('textarea.chat-textarea'), target: view.el.querySelector('textarea.chat-textarea'),
keyCode: 1 keyCode: 1
}); });
expect(view.setChatState).toHaveBeenCalled(); expect(view.setChatState).toHaveBeenCalled();
expect(view.model.get('chat_state')).toBe('composing'); expect(view.model.get('chat_state')).toBe('composing');
view.keyPressed({ view.keyPressed({
target: $(view.el).find('textarea.chat-textarea'), target: view.el.querySelector('textarea.chat-textarea'),
keyCode: 1 keyCode: 1
}); });
expect(view.model.get('chat_state')).toBe('composing'); expect(view.model.get('chat_state')).toBe('composing');
@ -921,33 +921,25 @@
contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost'; contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
test_utils.openChatBoxFor(_converse, contact_jid); test_utils.openChatBoxFor(_converse, contact_jid);
view = _converse.chatboxviews.get(contact_jid); view = _converse.chatboxviews.get(contact_jid);
return test_utils.waitUntil(function () { return test_utils.waitUntil(() => view.model.get('chat_state') === 'active', 500);
return view.model.get('chat_state') === 'active';
}, 500);
}).then(function () { }).then(function () {
console.log('chat_state set to active'); console.log('chat_state set to active');
view = _converse.chatboxviews.get(contact_jid); view = _converse.chatboxviews.get(contact_jid);
expect(view.model.get('chat_state')).toBe('active'); expect(view.model.get('chat_state')).toBe('active');
view.keyPressed({ view.keyPressed({
target: $(view.el).find('textarea.chat-textarea'), target: view.el.querySelector('textarea.chat-textarea'),
keyCode: 1 keyCode: 1
}); });
return test_utils.waitUntil(function () { return test_utils.waitUntil(() => view.model.get('chat_state') === 'composing', 500);
return view.model.get('chat_state') === 'composing';
}, 500);
}).then(function () { }).then(function () {
console.log('chat_state set to composing'); console.log('chat_state set to composing');
view = _converse.chatboxviews.get(contact_jid); view = _converse.chatboxviews.get(contact_jid);
expect(view.model.get('chat_state')).toBe('composing'); expect(view.model.get('chat_state')).toBe('composing');
spyOn(_converse.connection, 'send'); spyOn(_converse.connection, 'send');
return test_utils.waitUntil(function () { return test_utils.waitUntil(() => view.model.get('chat_state') === 'paused', 500);
return view.model.get('chat_state') === 'paused';
}, 500);
}).then(function () { }).then(function () {
console.log('chat_state set to paused'); console.log('chat_state set to paused');
return test_utils.waitUntil(function () { return test_utils.waitUntil(() => view.model.get('chat_state') === 'inactive', 500);
return view.model.get('chat_state') === 'inactive';
}, 500);
}).then(function () { }).then(function () {
console.log('chat_state set to inactive'); console.log('chat_state set to inactive');
expect(_converse.connection.send).toHaveBeenCalled(); expect(_converse.connection.send).toHaveBeenCalled();
@ -1635,7 +1627,7 @@
return $(view.el).find('.chat-content').find('.chat-msg').length; return $(view.el).find('.chat-content').find('.chat-msg').length;
}, 1000).then(function () { }, 1000).then(function () {
expect(view.model.sendMessage).toHaveBeenCalled(); 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( expect(msg.html()).toEqual(
'<a target="_blank" rel="noopener" href="https://www.openstreetmap.org/?mlat=37.786971&amp;'+ '<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'+ 'mlon=-122.399677#map=18/37.786971/-122.399677">https://www.openstreetmap.org/?mlat=37.7869'+

View File

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

View File

@ -364,7 +364,7 @@
}, 1000); }, 1000);
}).then(function () { }).then(function () {
// Check that the image renders // 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'+ '<!-- src/templates/image.html -->\n'+
'<a href="http://localhost:8000/logo/conversejs-filled.svg" target="_blank" rel="noopener">'+ '<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">'+ '<img class="chat-image img-thumbnail" src="http://localhost:8000/logo/conversejs-filled.svg">'+
@ -472,7 +472,7 @@
}, 1000); }, 1000);
}).then(function () { }).then(function () {
// Check that the image renders // 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'+ '<!-- src/templates/image.html -->\n'+
'<a href="http://localhost:8000/logo/conversejs-filled.svg" target="_blank" rel="noopener">'+ '<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>') '<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'); expect(view.el.querySelector('.chat-content progress').getAttribute('value')).toBe('0.5');
message.set('progress', 1); message.set('progress', 1);
expect(view.el.querySelector('.chat-content progress').getAttribute('value')).toBe('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(); done();
}); });
var sent_stanza; var sent_stanza;

File diff suppressed because it is too large Load Diff

View File

@ -47,7 +47,7 @@
"<presence xmlns='jabber:client'>"+ "<presence xmlns='jabber:client'>"+
"<status>Hello world</status>"+ "<status>Hello world</status>"+
"<priority>0</priority>"+ "<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>" "</presence>"
); );
_converse.priority = 2; _converse.priority = 2;
@ -57,7 +57,7 @@
"<show>away</show>"+ "<show>away</show>"+
"<status>Going jogging</status>"+ "<status>Going jogging</status>"+
"<priority>2</priority>"+ "<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>" "</presence>"
); );
@ -68,7 +68,7 @@
"<show>dnd</show>"+ "<show>dnd</show>"+
"<status>Doing taxes</status>"+ "<status>Doing taxes</status>"+
"<priority>0</priority>"+ "<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>" "</presence>"
); );
})); }));
@ -97,7 +97,7 @@
.toBe("<presence xmlns='jabber:client'>"+ .toBe("<presence xmlns='jabber:client'>"+
"<status>My custom status</status>"+ "<status>My custom status</status>"+
"<priority>0</priority>"+ "<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>") "</presence>")
return test_utils.waitUntil(function () { return test_utils.waitUntil(function () {
@ -113,7 +113,7 @@
modal.el.querySelector('[type="submit"]').click(); modal.el.querySelector('[type="submit"]').click();
expect(_converse.connection.send.calls.mostRecent().args[0].toLocaleString()) 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>"+ .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>") "</presence>")
done(); 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( it("has an info icon which opens a details modal when clicked", mock.initConverseWithPromises(
null, ['rosterGroupsFetched'], null, ['rosterGroupsFetched'],
@ -114,7 +145,7 @@
const room_els = _converse.rooms_list_view.el.querySelectorAll(".open-room"); const room_els = _converse.rooms_list_view.el.querySelectorAll(".open-room");
expect(room_els.length).toBe(1); 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(); info_el.click();
const modal = view.model.room_details_modal; const modal = view.model.room_details_modal;
@ -122,8 +153,8 @@
}).then(() => { }).then(() => {
const modal = view.model.room_details_modal; const modal = view.model.room_details_modal;
let els = modal.el.querySelectorAll('p.room-info'); let els = modal.el.querySelectorAll('p.room-info');
expect(els[0].textContent).toBe("Room address (JID): coven@chat.shakespeare.lit") expect(els[0].textContent).toBe("Name: A Dark Cave")
expect(els[1].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[2].textContent).toBe("Description: This is the description")
expect(els[3].textContent).toBe("Online users: 1") expect(els[3].textContent).toBe("Online users: 1")
const features_list = modal.el.querySelector('.features-list'); const features_list = modal.el.querySelector('.features-list');
@ -149,8 +180,17 @@
els = modal.el.querySelectorAll('p.room-info'); els = modal.el.querySelectorAll('p.room-info');
expect(els[3].textContent).toBe("Online users: 2") 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(); done();
}); }).catch(_.partial(_converse.log, _, Strophe.LogLevel.FATAL));
})); }));
it("can be closed", mock.initConverseWithPromises( it("can be closed", mock.initConverseWithPromises(
@ -173,7 +213,7 @@
var close_el = _converse.rooms_list_view.el.querySelector(".close-room"); var close_el = _converse.rooms_list_view.el.querySelector(".close-room");
close_el.click(); close_el.click();
expect(window.confirm).toHaveBeenCalledWith( 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"); room_els = _converse.rooms_list_view.el.querySelectorAll(".open-room");
expect(room_els.length).toBe(0); expect(room_els.length).toBe(0);
expect(_converse.chatboxes.length).toBe(1); expect(_converse.chatboxes.length).toBe(1);
@ -206,7 +246,7 @@
type: 'groupchat' type: 'groupchat'
}).c('body').t('foo').tree()); }).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( var room_el = _converse.rooms_list_view.el.querySelector(
".available-chatroom" ".available-chatroom"
); );

View File

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

View File

@ -67,7 +67,7 @@
{ __ } = _converse; { __ } = _converse;
const bookmark_button = tpl_chatroom_bookmark_toggle( const bookmark_button = tpl_chatroom_bookmark_toggle(
_.assignIn(this.model.toJSON(), { _.assignIn(this.model.toJSON(), {
info_toggle_bookmark: __('Bookmark this room'), info_toggle_bookmark: __('Bookmark this groupchat'),
bookmarked: this.model.get('bookmarked') bookmarked: this.model.get('bookmarked')
})); }));
const close_button = this.el.querySelector('.close-chatbox-button'); const close_button = this.el.querySelector('.close-chatbox-button');
@ -143,10 +143,10 @@
body.insertAdjacentHTML( body.insertAdjacentHTML(
'beforeend', 'beforeend',
tpl_chatroom_bookmark_form({ tpl_chatroom_bookmark_form({
heading: __('Bookmark this room'), heading: __('Bookmark this groupchat'),
label_name: __('The name for this bookmark:'), label_name: __('The name for this bookmark:'),
label_autojoin: __('Would you like this room to be automatically joined upon startup?'), label_autojoin: __('Would you like this groupchat to be automatically joined upon startup?'),
label_nick: __('What should your nickname for this room be?'), label_nick: __('What should your nickname for this groupchat be?'),
default_nick: this.model.get('nick'), default_nick: this.model.get('nick'),
label_submit: __('Save'), label_submit: __('Save'),
label_cancel: __('Cancel') label_cancel: __('Cancel')
@ -410,13 +410,13 @@
'hidden': _converse.hide_open_bookmarks && 'hidden': _converse.hide_open_bookmarks &&
_converse.chatboxes.where({'jid': this.model.get('jid')}).length, _converse.chatboxes.where({'jid': this.model.get('jid')}).length,
'bookmarked': true, 'bookmarked': true,
'info_leave_room': __('Leave this room'), 'info_leave_room': __('Leave this groupchat'),
'info_remove': __('Remove this bookmark'), 'info_remove': __('Remove this bookmark'),
'info_remove_bookmark': __('Unbookmark this room'), 'info_remove_bookmark': __('Unbookmark this groupchat'),
'info_title': __('Show more information on this room'), 'info_title': __('Show more information on this groupchat'),
'jid': this.model.get('jid'), 'jid': this.model.get('jid'),
'name': Strophe.xmlunescape(this.model.get('name')), '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 { $msg, Backbone, Promise, Strophe, b64_sha1, moment, sizzle, utils, _ } = converse.env;
const u = converse.env.utils; const u = converse.env.utils;
Strophe.addNamespace('MESSAGE_CORRECT', 'urn:xmpp:message-correct:0');
converse.plugins.add('converse-chatboxes', { converse.plugins.add('converse-chatboxes', {
@ -43,7 +45,7 @@
* loaded by converse.js's plugin machinery. * loaded by converse.js's plugin machinery.
*/ */
const { _converse } = this, const { _converse } = this,
{ __ } = _converse; { __ } = _converse;
// Configuration values for this plugin // Configuration values for this plugin
// ==================================== // ====================================
@ -215,9 +217,11 @@
}, false); }, false);
xhr.onerror = () => { xhr.onerror = () => {
let message = __('Sorry, could not succesfully upload your file.'); let message;
if (xhr.responseText) { 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({ this.save({
'type': 'error', 'type': 'error',
@ -290,6 +294,23 @@
return this.vcard.get('fullname') || this.get('jid'); 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) { createMessageStanza (message) {
/* Given a _converse.Message Backbone.Model, return the XML /* Given a _converse.Message Backbone.Model, return the XML
* stanza that represents it. * stanza that represents it.
@ -301,7 +322,7 @@
'from': _converse.connection.jid, 'from': _converse.connection.jid,
'to': this.get('jid'), 'to': this.get('jid'),
'type': this.get('message_type'), '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('body').t(message.get('message')).up()
.c(_converse.ACTIVE, {'xmlns': Strophe.NS.CHATSTATES}).up(); .c(_converse.ACTIVE, {'xmlns': Strophe.NS.CHATSTATES}).up();
@ -315,6 +336,12 @@
if (message.get('file')) { if (message.get('file')) {
stanza.c('x', {'xmlns': Strophe.NS.OUTOFBAND}).c('url').t(message.get('message')).up(); 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; return stanza;
}, },
@ -357,8 +384,20 @@
* Parameters: * Parameters:
* (Message) message - The chat message * (Message) message - The chat message
*/ */
const message = this.messages.create(attrs); let message = this.messages.findWhere('correcting')
this.sendMessageStanza(this.createMessageStanza(message)); 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 () { sendChatState () {
@ -410,24 +449,12 @@
}).catch(_.partial(_converse.log, _, Strophe.LogLevel.FATAL)); }).catch(_.partial(_converse.log, _, Strophe.LogLevel.FATAL));
}, },
getMessageBody (message) { getMessageAttributesFromStanza (stanza, original_stanza) {
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) {
/* Parses a passed in message stanza and returns an object /* Parses a passed in message stanza and returns an object
* of attributes. * of attributes.
* *
* Parameters: * Parameters:
* (XMLElement) message - The message stanza * (XMLElement) stanza - The message stanza
* (XMLElement) delay - The <delay> node from the * (XMLElement) delay - The <delay> node from the
* stanza, if there was one. * stanza, if there was one.
* (XMLElement) original_stanza - The original stanza, * (XMLElement) original_stanza - The original stanza,
@ -439,24 +466,24 @@
archive = sizzle(`result[xmlns="${Strophe.NS.MAM}"]`, original_stanza).pop(), archive = sizzle(`result[xmlns="${Strophe.NS.MAM}"]`, original_stanza).pop(),
spoiler = sizzle(`spoiler[xmlns="${Strophe.NS.SPOILER}"]`, original_stanza).pop(), spoiler = sizzle(`spoiler[xmlns="${Strophe.NS.SPOILER}"]`, original_stanza).pop(),
delay = sizzle(`delay[xmlns="${Strophe.NS.DELAY}"]`, original_stanza).pop(), delay = sizzle(`delay[xmlns="${Strophe.NS.DELAY}"]`, original_stanza).pop(),
chat_state = message.getElementsByTagName(_converse.COMPOSING).length && _converse.COMPOSING || chat_state = stanza.getElementsByTagName(_converse.COMPOSING).length && _converse.COMPOSING ||
message.getElementsByTagName(_converse.PAUSED).length && _converse.PAUSED || stanza.getElementsByTagName(_converse.PAUSED).length && _converse.PAUSED ||
message.getElementsByTagName(_converse.INACTIVE).length && _converse.INACTIVE || stanza.getElementsByTagName(_converse.INACTIVE).length && _converse.INACTIVE ||
message.getElementsByTagName(_converse.ACTIVE).length && _converse.ACTIVE || stanza.getElementsByTagName(_converse.ACTIVE).length && _converse.ACTIVE ||
message.getElementsByTagName(_converse.GONE).length && _converse.GONE; stanza.getElementsByTagName(_converse.GONE).length && _converse.GONE;
const attrs = { const attrs = {
'chat_state': chat_state, 'chat_state': chat_state,
'is_archived': !_.isNil(archive), 'is_archived': !_.isNil(archive),
'is_delayed': !_.isNil(delay), 'is_delayed': !_.isNil(delay),
'is_spoiler': !_.isNil(spoiler), 'is_spoiler': !_.isNil(spoiler),
'message': this.getMessageBody(message) || undefined, 'message': _converse.chatboxes.getMessageBody(stanza) || undefined,
'msgid': message.getAttribute('id'), 'msgid': stanza.getAttribute('id'),
'time': delay ? delay.getAttribute('stamp') : moment().format(), 'time': delay ? delay.getAttribute('stamp') : moment().format(),
'type': message.getAttribute('type') 'type': stanza.getAttribute('type')
}; };
if (attrs.type === 'groupchat') { if (attrs.type === 'groupchat') {
attrs.from = message.getAttribute('from'); attrs.from = stanza.getAttribute('from');
attrs.nick = Strophe.unescapeNode(Strophe.getResourceFromJid(attrs.from)); attrs.nick = Strophe.unescapeNode(Strophe.getResourceFromJid(attrs.from));
if (attrs.from === this.get('nick')) { if (attrs.from === this.get('nick')) {
attrs.sender = 'me'; attrs.sender = 'me';
@ -464,7 +491,7 @@
attrs.sender = 'them'; attrs.sender = 'them';
} }
} else { } else {
attrs.from = Strophe.getBareJidFromJid(message.getAttribute('from')); attrs.from = Strophe.getBareJidFromJid(stanza.getAttribute('from'));
if (attrs.from === _converse.bare_jid) { if (attrs.from === _converse.bare_jid) {
attrs.sender = 'me'; attrs.sender = 'me';
attrs.fullname = _converse.xmppstatus.get('fullname'); attrs.fullname = _converse.xmppstatus.get('fullname');
@ -473,7 +500,7 @@
attrs.fullname = this.get('fullname'); 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_url'] = xform.querySelector('url').textContent;
attrs['oob_desc'] = xform.querySelector('url').textContent; attrs['oob_desc'] = xform.querySelector('url').textContent;
}); });
@ -591,19 +618,29 @@
return true; 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" /* Handler method for all incoming single-user chat "message"
* stanzas. * stanzas.
* *
* Parameters: * Parameters:
* (XMLElement) message - The incoming message stanza * (XMLElement) stanza - The incoming message stanza
*/ */
let from_jid = message.getAttribute('from'), let from_jid = stanza.getAttribute('from'),
to_jid = message.getAttribute('to'); to_jid = stanza.getAttribute('to');
const to_resource = Strophe.getResourceFromJid(to_jid);
const original_stanza = message,
to_resource = Strophe.getResourceFromJid(to_jid),
is_carbon = !_.isNull(message.querySelector(`received[xmlns="${Strophe.NS.CARBONS}"]`));
if (_converse.filter_by_resource && (to_resource && to_resource !== _converse.resource)) { if (_converse.filter_by_resource && (to_resource && to_resource !== _converse.resource)) {
_converse.log( _converse.log(
@ -611,7 +648,7 @@
Strophe.LogLevel.INFO Strophe.LogLevel.INFO
); );
return true; return true;
} else if (utils.isHeadlineMessage(_converse, message)) { } else if (utils.isHeadlineMessage(_converse, stanza)) {
// XXX: Ideally we wouldn't have to check for headline // XXX: Ideally we wouldn't have to check for headline
// messages, but Prosody sends headline messages with the // messages, but Prosody sends headline messages with the
// wrong type ('chat'), so we need to filter them out here. // wrong type ('chat'), so we need to filter them out here.
@ -621,23 +658,28 @@
); );
return true; return true;
} }
const forwarded = message.querySelector('forwarded');
const forwarded = stanza.querySelector('forwarded'),
original_stanza = stanza;
if (!_.isNull(forwarded)) { if (!_.isNull(forwarded)) {
const forwarded_message = forwarded.querySelector('message'); const forwarded_message = forwarded.querySelector('message'),
const forwarded_from = forwarded_message.getAttribute('from'); 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) { if (is_carbon && Strophe.getBareJidFromJid(forwarded_from) !== from_jid) {
// Prevent message forging via carbons // Prevent message forging via carbons
// https://xmpp.org/extensions/xep-0280.html#security // https://xmpp.org/extensions/xep-0280.html#security
return true; return true;
} }
message = forwarded_message; stanza = forwarded_message;
from_jid = message.getAttribute('from'); from_jid = stanza.getAttribute('from');
to_jid = message.getAttribute('to'); to_jid = stanza.getAttribute('to');
} }
const from_bare_jid = Strophe.getBareJidFromJid(from_jid), const from_bare_jid = Strophe.getBareJidFromJid(from_jid),
from_resource = Strophe.getResourceFromJid(from_jid), from_resource = Strophe.getResourceFromJid(from_jid),
is_me = from_bare_jid === _converse.bare_jid; is_me = from_bare_jid === _converse.bare_jid;
let contact_jid; let contact_jid;
if (is_me) { if (is_me) {
@ -650,16 +692,14 @@
const attrs = { const attrs = {
'fullname': _.get(_converse.api.contacts.get(contact_jid), 'attributes.fullname') 'fullname': _.get(_converse.api.contacts.get(contact_jid), 'attributes.fullname')
} }
const chatbox = this.getChatBox(contact_jid, attrs, !_.isNull(message.querySelector('body'))), const chatbox = this.getChatBox(contact_jid, attrs, !_.isNull(stanza.querySelector('body')));
msgid = message.getAttribute('id'); if (chatbox && !chatbox.handleMessageCorrection(stanza)) {
const msgid = stanza.getAttribute('id'),
if (chatbox) { message = msgid && chatbox.messages.findWhere({msgid});
const messages = msgid && chatbox.messages.findWhere({msgid}) || []; if (!message) {
if (_.isEmpty(messages)) { // Only create the message when we're sure it's not a duplicate
// Only create the message when we're sure it's not a
// duplicate
chatbox.incrementUnreadMsgCounter(original_stanza); chatbox.incrementUnreadMsgCounter(original_stanza);
chatbox.createMessage(message, original_stanza); chatbox.createMessage(stanza, original_stanza);
} }
} }
_converse.emit('message', {'stanza': original_stanza, 'chatbox': chatbox}); _converse.emit('message', {'stanza': original_stanza, 'chatbox': chatbox});
@ -813,6 +853,7 @@
_converse.on('addClientFeatures', () => { _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.HTTPUPLOAD);
_converse.api.disco.own.features.add(Strophe.NS.OUTOFBAND); _converse.api.disco.own.features.add(Strophe.NS.OUTOFBAND);
}); });

View File

@ -9,7 +9,6 @@
"bootstrap", "bootstrap",
"emojione", "emojione",
"xss", "xss",
"templates/action.html",
"templates/chatbox.html", "templates/chatbox.html",
"templates/chatbox_head.html", "templates/chatbox_head.html",
"templates/chatbox_message_form.html", "templates/chatbox_message_form.html",
@ -33,7 +32,6 @@
bootstrap, bootstrap,
emojione, emojione,
xss, xss,
tpl_action,
tpl_chatbox, tpl_chatbox,
tpl_chatbox_head, tpl_chatbox_head,
tpl_chatbox_message_form, tpl_chatbox_message_form,
@ -54,6 +52,8 @@
const u = converse.env.utils; const u = converse.env.utils;
const KEY = { const KEY = {
ENTER: 13, ENTER: 13,
UP_ARROW: 38,
DOWN_ARROW: 40,
FORWARD_SLASH: 47 FORWARD_SLASH: 47
}; };
@ -237,7 +237,7 @@
_converse.UserDetailsModal = _converse.BootstrapModal.extend({ _converse.UserDetailsModal = _converse.BootstrapModal.extend({
events: { events: {
'click button.remove-contact': 'removeContact', 'click button.remove-contact': 'removeContact',
'click button.refresh-contact': 'refreshContact' 'click button.refresh-contact': 'refreshContact'
}, },
@ -321,6 +321,7 @@
events: { events: {
'change input.fileupload': 'onFileSelection', 'change input.fileupload': 'onFileSelection',
'click .chat-msg__action-edit': 'onMessageEditButtonClicked',
'click .chatbox-navback': 'showControlBox', 'click .chatbox-navback': 'showControlBox',
'click .close-chatbox-button': 'close', 'click .close-chatbox-button': 'close',
'click .new-msgs-indicator': 'viewUnreadMessages', 'click .new-msgs-indicator': 'viewUnreadMessages',
@ -333,8 +334,8 @@
'click .toggle-smiley ul.emoji-picker li': 'insertEmoji', 'click .toggle-smiley ul.emoji-picker li': 'insertEmoji',
'click .toggle-smiley': 'toggleEmojiMenu', 'click .toggle-smiley': 'toggleEmojiMenu',
'click .upload-file': 'toggleFileUpload', 'click .upload-file': 'toggleFileUpload',
'keypress .chat-textarea': 'keyPressed', 'input .chat-textarea': 'inputChanged',
'input .chat-textarea': 'inputChanged' 'keydown .chat-textarea': 'keyPressed'
}, },
initialize () { initialize () {
@ -746,19 +747,19 @@
date = moment(el.getAttribute('data-isodate')), date = moment(el.getAttribute('data-isodate')),
next_el = el.nextElementSibling; 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 && previous_el.getAttribute('data-from') === from &&
date.isBefore(moment(previous_el.getAttribute('data-isodate')).add(10, 'minutes'))) { 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 (!next_el) { return; }
if (!u.hasClass('chat-action', 'el') && if (!u.hasClass('chat-msg--action', 'el') &&
next_el.getAttribute('data-from') === from && next_el.getAttribute('data-from') === from &&
moment(next_el.getAttribute('data-isodate')).isBefore(date.add(10, 'minutes'))) { 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 { } 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. * (Object) message - The message Backbone object that was added.
*/ */
this.showMessage(message); this.showMessage(message);
if (message.get('correcting')) {
this.insertIntoTextArea(message.get('message'), true);
}
_converse.emit('messageAdded', { _converse.emit('messageAdded', {
'message': message, 'message': message,
'chatbox': this.model 'chatbox': this.model
@ -911,8 +914,14 @@
keyPressed (ev) { keyPressed (ev) {
/* Event handler for when a key is pressed in a chat box textarea. /* 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); 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) { } 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 // Set chat state to composing if keyCode is not a forward-slash
// (which would imply an internal command and not a message). // (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) { inputChanged (ev) {
ev.target.style.height = 'auto'; // Fixes weirdness ev.target.style.height = 'auto'; // Fixes weirdness
ev.target.style.height = (ev.target.scrollHeight) + 'px'; ev.target.style.height = (ev.target.scrollHeight) + 'px';
@ -936,14 +1010,18 @@
return this; return this;
}, },
insertIntoTextArea (value) { insertIntoTextArea (value, replace=false) {
const textbox_el = this.el.querySelector('.chat-textarea'); const textarea = this.el.querySelector('.chat-textarea');
let existing = textbox_el.value; if (replace) {
if (existing && (existing[existing.length-1] !== ' ')) { textarea.value = value;
existing = existing + ' '; } else {
let existing = textarea.value;
if (existing && (existing[existing.length-1] !== ' ')) {
existing = existing + ' ';
}
textarea.value = existing+value+' ';
} }
textbox_el.value = existing+value+' '; textarea.focus()
textbox_el.focus()
}, },
createEmojiPicker () { createEmojiPicker () {
@ -1017,13 +1095,13 @@
let text; let text;
if (u.isVisible(this.el)) { if (u.isVisible(this.el)) {
if (show === 'offline') { if (show === 'offline') {
text = fullname+' '+__('has gone offline'); text = __('%1$s has gone offline', fullname);
} else if (show === 'away') { } else if (show === 'away') {
text = fullname+' '+__('has gone away'); text = __('%1$s has gone away', fullname);
} else if ((show === 'dnd')) { } else if ((show === 'dnd')) {
text = fullname+' '+__('is busy'); text = __('%1$s is busy', fullname);
} else if (show === 'online') { } else if (show === 'online') {
text = fullname+' '+__('is online'); text = __('%1$s is online', fullname);
} }
if (text) { if (text) {
this.content.insertAdjacentHTML( this.content.insertAdjacentHTML(

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -232,10 +232,20 @@
'user_id': Strophe.getNodeFromJid(jid) 'user_id': Strophe.getNodeFromJid(jid)
}, attributes)); }, attributes));
this.setChatBox();
this.presence.on('change:show', () => _converse.emit('contactPresenceChanged', this)); this.presence.on('change:show', () => _converse.emit('contactPresenceChanged', this));
this.presence.on('change:show', () => this.trigger('presenceChanged')); 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 () { getDisplayName () {
return this.vcard.get('fullname') || this.get('jid'); return this.vcard.get('fullname') || this.get('jid');
}, },
@ -450,7 +460,7 @@
* (String) name - The name of that user * (String) name - The name of that user
* (Array of Strings) groups - Any roster groups the user might belong to * (Array of Strings) groups - Any roster groups the user might belong to
* (Function) callback - A function to call once the IQ is returned * (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; name = _.isEmpty(name)? jid: name;
const iq = $iq({type: 'set'}) const iq = $iq({type: 'set'})
@ -798,6 +808,16 @@
/********** Event Handlers *************/ /********** 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('beforeTearDown', _converse.unregisterPresenceHandler());
_converse.api.listen.on('afterTearDown', () => { _converse.api.listen.on('afterTearDown', () => {

View File

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

View File

@ -50,7 +50,7 @@
if (chatbox.get('id') === 'controlbox') { if (chatbox.get('id') === 'controlbox') {
return true; return true;
} }
if (_.includes(['mobile', 'fullscreen', 'embedded'], _converse.view_mode)) { if (_converse.isSingleton()) {
const any_chats_visible = _converse.chatboxes const any_chats_visible = _converse.chatboxes
.filter((cb) => cb.get('id') != 'controlbox') .filter((cb) => cb.get('id') != 'controlbox')
.filter((cb) => !cb.get('hidden')).length > 0; .filter((cb) => !cb.get('hidden')).length > 0;
@ -67,7 +67,8 @@
createChatBox (jid, attrs) { createChatBox (jid, attrs) {
/* Make sure new chat boxes are hidden by default. */ /* 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 = attrs || {};
attrs.hidden = true; attrs.hidden = true;
} }
@ -77,7 +78,8 @@
ChatBoxView: { ChatBoxView: {
shouldShowOnTextMessage () { shouldShowOnTextMessage () {
if (_.includes(['mobile', 'fullscreen', 'embedded'], this.__super__._converse.view_mode)) { const { _converse } = this.__super__;
if (_converse.isSingleton()) {
return false; return false;
} else { } else {
return this.__super__.shouldShowOnTextMessage.apply(this, arguments); return this.__super__.shouldShowOnTextMessage.apply(this, arguments);
@ -89,7 +91,8 @@
* time. So before opening a chat, we make sure all other * time. So before opening a chat, we make sure all other
* chats are hidden. * 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); _.each(this.__super__._converse.chatboxviews.xget(this.model.get('id')), hideChat);
this.model.set('hidden', false); this.model.set('hidden', false);
} }
@ -99,7 +102,8 @@
ChatRoomView: { ChatRoomView: {
show (focus) { 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); _.each(this.__super__._converse.chatboxviews.xget(this.model.get('id')), hideChat);
this.model.set('hidden', false); 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}}}"> <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="list-item-link 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-action remove-bookmark fa fa-bookmark align-self-center {[ if (o.bookmarked) { ]} button-on {[ } ]}"
data-room-jid="{{{o.jid}}}" data-bookmark-name="{{{o.name}}}" data-room-jid="{{{o.jid}}}" data-bookmark-name="{{{o.name}}}"
title="{{{o.info_remove_bookmark}}}" href="#">&nbsp;</a> 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> </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 class="fa {[ if (o.toggle_state === o._converse.OPENED) { ]} fa-caret-down {[ } else { ]} fa-caret-right {[ } ]}">
</span> {{{o.label_bookmarks}}}</a> </span> {{{o.label_bookmarks}}}</a>
<div class="items-list bookmarks rooms-list {[ if (o.toggle_state !== o._converse.OPENED) { ]} hidden {[ } ]}"></div> <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="flyout box-flyout">
<div class="chat-head chat-head-chatroom row no-gutters"></div> <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> </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-dialog" role="document">
<div class="modal-content"> <div class="modal-content">
<div class="modal-header"> <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> <button type="button" class="close" data-dismiss="modal" aria-label="{{{o.label_close}}}"><span aria-hidden="true">&times;</span></button>
</div> </div>
<div class="modal-body"> <div class="modal-body">
<div class="room-info"> <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.__('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> <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.__('Online users')}}}</strong>: {{{o.num_occupants}}}</p>
<p class="room-info"><strong>{{{o.__('Features')}}}</strong>: <p class="room-info"><strong>{{{o.__('Features')}}}</strong>:
<div class="chatroom-features"> <div class="chatroom-features">
@ -58,6 +62,9 @@
</p> </p>
</div> </div>
</div> </div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">{{{o.__('Close')}}}</button>
</div>
</div> </div>
</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"> <ul class="features-list">
{[ if (o.passwordprotected) { ]} {[ 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) { ]} {[ 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) { ]} {[ 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) { ]} {[ 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) { ]} {[ 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) { ]} {[ 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) { ]} {[ 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) { ]} {[ 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) { ]} {[ 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) { ]} {[ 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> <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) { ]} {[ 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) { ]} {[ 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) { ]} {[ 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> <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}}}"> <div class="message chat-msg" data-isodate="{{{o.time}}}" data-msgid="{{{o.msgid}}}">
<canvas class="avatar" height="36" width="36"></canvas> <canvas class="avatar chat-msg__avatar" height="36" width="36"></canvas>
<div class="chat-msg-content"> <div class="chat-msg__content">
<span class="chat-msg-text">Uploading file: <strong>{{{o.file.name}}}</strong>, {{{o.filesize}}}</span> <span class="chat-msg__text">Uploading file: <strong>{{{o.file.name}}}</strong>, {{{o.filesize}}}</span>
<progress value="{{{o.progress}}}"/> <progress value="{{{o.progress}}}"/>
</div> </div>
</div> </div>

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