Merge branch 'master' of github.com:jcbrand/converse.js
This commit is contained in:
commit
6553b5ba73
23
CHANGES.rst
23
CHANGES.rst
@ -4,22 +4,27 @@ Changelog
|
|||||||
0.3 (unreleased)
|
0.3 (unreleased)
|
||||||
----------------
|
----------------
|
||||||
|
|
||||||
- Add vCard support [jcbrand]
|
- Add vCard support
|
||||||
- Remember custom status messages upon reload. [jcbrand]
|
[jcbrand]
|
||||||
- Remove jquery-ui dependency. [jcbrand]
|
- Remember custom status messages upon reload.
|
||||||
|
[jcbrand]
|
||||||
|
- Remove jquery-ui dependency.
|
||||||
|
[jcbrand]
|
||||||
- Use backbone.localStorage to store the contacts roster, open chatboxes and
|
- Use backbone.localStorage to store the contacts roster, open chatboxes and
|
||||||
chat messages. [jcbrand]
|
chat messages.
|
||||||
- Fixed user status handling, which wasn't 100% according to the
|
[jcbrand]
|
||||||
spec. [jcbrand]
|
- Fixed user status handling, which wasn't 100% according to the spec.
|
||||||
- Separate messages according to day in chats. [jcbrand]
|
[jcbrand]
|
||||||
|
- Separate messages according to day in chats.
|
||||||
|
[jcbrand]
|
||||||
- Add support for specifying the BOSH bind URL as configuration setting.
|
- Add support for specifying the BOSH bind URL as configuration setting.
|
||||||
[jcbrand]
|
[jcbrand]
|
||||||
- Improve the message counter to only increment when the window is not focused
|
- Improve the message counter to only increment when the window is not focused
|
||||||
[witekdev]
|
[witekdev]
|
||||||
- Make fetching of list of chatrooms on a server a configuration option.
|
- Make fetching of list of chatrooms on a server a configuration option.
|
||||||
[jcbrand]
|
[jcbrand]
|
||||||
- Use service discovery to show whether a chatroom is password protected as
|
- Use service discovery to show all available features on a room.
|
||||||
well as its number of occupents. [jcbrand]
|
[jcbrand]
|
||||||
|
|
||||||
|
|
||||||
0.2 (2013-03-28)
|
0.2 (2013-03-28)
|
||||||
|
@ -57,6 +57,7 @@
|
|||||||
* (String) nick - Optional nickname to use in the chat room.
|
* (String) nick - Optional nickname to use in the chat room.
|
||||||
* (Function) msg_handler_cb - The function call to handle messages from the specified chat room.
|
* (Function) msg_handler_cb - The function call to handle messages from the specified chat room.
|
||||||
* (Function) pres_handler_cb - The function call back to handle presence in the chat room.
|
* (Function) pres_handler_cb - The function call back to handle presence in the chat room.
|
||||||
|
* (Function) roster_cb - The function call back to handle roster changes in the chat room.
|
||||||
* (String) password - The optional password to use. (password protected rooms only)
|
* (String) password - The optional password to use. (password protected rooms only)
|
||||||
*/
|
*/
|
||||||
var msg, room_nick, _this = this;
|
var msg, room_nick, _this = this;
|
||||||
|
48
converse.css
48
converse.css
@ -92,7 +92,11 @@ img.spinner {
|
|||||||
display: block;
|
display: block;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
padding: 0.5em 0 0 0.5em;
|
padding: 0.5em 0 0 0.5em;
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul.participant-list li.moderator {
|
||||||
|
color: #FE0007;
|
||||||
}
|
}
|
||||||
|
|
||||||
.chatroom form.sendXMPPMessage {
|
.chatroom form.sendXMPPMessage {
|
||||||
@ -223,6 +227,7 @@ div.chat-title {
|
|||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
text-shadow: rgba(0,0,0,0.51) 0 -1px 0;
|
text-shadow: rgba(0,0,0,0.51) 0 -1px 0;
|
||||||
|
height: 1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.chat-head-chatbox,
|
.chat-head-chatbox,
|
||||||
@ -441,7 +446,7 @@ form.search-xmpp-contact input {
|
|||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
width: 160px;
|
width: 170px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#available-chatrooms dt,
|
#available-chatrooms dt,
|
||||||
@ -468,6 +473,41 @@ dd.available-chatroom,
|
|||||||
text-shadow: 0 1px 0 rgba(250, 250, 250, 1);
|
text-shadow: 0 1px 0 rgba(250, 250, 250, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.room-info {
|
||||||
|
font-size: 11px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
p.room-info {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
display: block;
|
||||||
|
white-space: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
a.room-info {
|
||||||
|
background: url('images/information.png') no-repeat right top;
|
||||||
|
width: 22px;
|
||||||
|
float: right;
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
a.open-room {
|
||||||
|
display: inline-block;
|
||||||
|
white-space: nowrap;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
overflow-x: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
dd.available-chatroom:hover a.room-info {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
dd.available-chatroom:hover a.open-room {
|
||||||
|
width: 75%;
|
||||||
|
}
|
||||||
|
|
||||||
#converse-roster dd a.remove-xmpp-contact {
|
#converse-roster dd a.remove-xmpp-contact {
|
||||||
background: url('images/delete_icon.png') no-repeat right top;
|
background: url('images/delete_icon.png') no-repeat right top;
|
||||||
padding: 0 0 1em 0;
|
padding: 0 0 1em 0;
|
||||||
@ -477,11 +517,11 @@ dd.available-chatroom,
|
|||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
#converse-roster dd:hover *[class*="remove-xmpp-contact"] {
|
#converse-roster dd:hover a.remove-xmpp-contact {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
}
|
}
|
||||||
|
|
||||||
#converse-roster dd:hover *[class*="open-chat"] {
|
#converse-roster dd:hover a.open-chat {
|
||||||
width: 75%;
|
width: 75%;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -541,10 +581,12 @@ form#converse-login {
|
|||||||
|
|
||||||
form#converse-login input {
|
form#converse-login input {
|
||||||
display: block;
|
display: block;
|
||||||
|
width: 90%;
|
||||||
}
|
}
|
||||||
|
|
||||||
form#converse-login .login-submit {
|
form#converse-login .login-submit {
|
||||||
margin-top: 1em;
|
margin-top: 1em;
|
||||||
|
width: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
form.set-xmpp-status,
|
form.set-xmpp-status,
|
||||||
|
250
converse.js
250
converse.js
@ -684,14 +684,56 @@
|
|||||||
events: {
|
events: {
|
||||||
'submit form.add-chatroom': 'createChatRoom',
|
'submit form.add-chatroom': 'createChatRoom',
|
||||||
'click input#show-rooms': 'showRooms',
|
'click input#show-rooms': 'showRooms',
|
||||||
'click a.open-room': 'createChatRoom'
|
'click a.open-room': 'createChatRoom',
|
||||||
|
'click a.room-info': 'showRoomInfo'
|
||||||
},
|
},
|
||||||
room_template: _.template(
|
room_template: _.template(
|
||||||
'<dd class="available-chatroom">' +
|
'<dd class="available-chatroom">'+
|
||||||
'<a class="open-room {{classes}}" data-room-jid="{{jid}}"' +
|
'<a class="open-room" data-room-jid="{{jid}}" title="Click to open this room" href="#">{{name}}</a>'+
|
||||||
' title="{{desc}}"' +
|
'<a class="room-info" data-room-jid="{{jid}}" title="Show more information on this room" href="#"> </a>'+
|
||||||
' href="#">' +
|
'</dd>'),
|
||||||
'{{name}}</a> {{occ}}</dd>'),
|
|
||||||
|
room_description_template: _.template(
|
||||||
|
'<div class="room-info">'+
|
||||||
|
'<p class="room-info"><strong>Description:</strong> {{desc}}</p>' +
|
||||||
|
'<p class="room-info"><strong>Occupants:</strong> {{occ}}</p>' +
|
||||||
|
'<p class="room-info"><strong>Features:</strong> <ul>'+
|
||||||
|
'{[ if (passwordprotected) { ]}' +
|
||||||
|
'<li class="room-info locked">Requires authentication</li>' +
|
||||||
|
'{[ } ]}' +
|
||||||
|
'{[ if (hidden) { ]}' +
|
||||||
|
'<li class="room-info">Hidden</li>' +
|
||||||
|
'{[ } ]}' +
|
||||||
|
'{[ if (membersonly) { ]}' +
|
||||||
|
'<li class="room-info">Requires an invitation</li>' +
|
||||||
|
'{[ } ]}' +
|
||||||
|
'{[ if (moderated) { ]}' +
|
||||||
|
'<li class="room-info">Moderated</li>' +
|
||||||
|
'{[ } ]}' +
|
||||||
|
'{[ if (nonanonymous) { ]}' +
|
||||||
|
'<li class="room-info">Non-anonymous</li>' +
|
||||||
|
'{[ } ]}' +
|
||||||
|
'{[ if (open) { ]}' +
|
||||||
|
'<li class="room-info">Open room</li>' +
|
||||||
|
'{[ } ]}' +
|
||||||
|
'{[ if (persistent) { ]}' +
|
||||||
|
'<li class="room-info">Permanent room</li>' +
|
||||||
|
'{[ } ]}' +
|
||||||
|
'{[ if (publicroom) { ]}' +
|
||||||
|
'<li class="room-info">Public</li>' +
|
||||||
|
'{[ } ]}' +
|
||||||
|
'{[ if (semianonymous) { ]}' +
|
||||||
|
'<li class="room-info">Semi-anonymous</li>' +
|
||||||
|
'{[ } ]}' +
|
||||||
|
'{[ if (temporary) { ]}' +
|
||||||
|
'<li class="room-info">Temporary room</li>' +
|
||||||
|
'{[ } ]}' +
|
||||||
|
'{[ if (unmoderated) { ]}' +
|
||||||
|
'<li class="room-info">Unmoderated</li>' +
|
||||||
|
'{[ } ]}' +
|
||||||
|
'</p>' +
|
||||||
|
'</div>'
|
||||||
|
),
|
||||||
|
|
||||||
tab_template: _.template('<li><a class="s" href="#chatrooms">Rooms</a></li>'),
|
tab_template: _.template('<li><a class="s" href="#chatrooms">Rooms</a></li>'),
|
||||||
|
|
||||||
@ -720,37 +762,23 @@
|
|||||||
converse.connection.muc.listRooms(
|
converse.connection.muc.listRooms(
|
||||||
this.muc_domain,
|
this.muc_domain,
|
||||||
$.proxy(function (iq) { // Success
|
$.proxy(function (iq) { // Success
|
||||||
var name, jid, i, that = this, $available_chatrooms = this.$el.find('#available-chatrooms');
|
var name, jid, i, fragment,
|
||||||
this.rdict = {};
|
that = this,
|
||||||
|
$available_chatrooms = this.$el.find('#available-chatrooms');
|
||||||
this.rooms = $(iq).find('query').find('item');
|
this.rooms = $(iq).find('query').find('item');
|
||||||
this.rooms.each(function (i) { that.rdict[$(this).attr('jid')] = this; });
|
|
||||||
this.fragment = document.createDocumentFragment();
|
|
||||||
if (this.rooms.length) {
|
if (this.rooms.length) {
|
||||||
$available_chatrooms.html('<dt>Rooms on '+this.muc_domain+'</dt>');
|
$available_chatrooms.html('<dt>Rooms on '+this.muc_domain+'</dt>');
|
||||||
_.each(this.rooms, $.proxy(function (room, idx) {
|
fragment = document.createDocumentFragment();
|
||||||
converse.connection.disco.info(
|
for (i=0; i<this.rooms.length; i++) {
|
||||||
$(room).attr('jid'),
|
name = Strophe.unescapeNode($(this.rooms[i]).attr('name')||$(this.rooms[i]).attr('jid'));
|
||||||
null,
|
jid = $(this.rooms[i]).attr('jid');
|
||||||
$.proxy(function (stanza) {
|
fragment.appendChild($(this.room_template({
|
||||||
var name = $(stanza).find('identity').attr('name');
|
'name':name,
|
||||||
var desc = $(stanza).find('field[var="muc#roominfo_description"] value').text();
|
'jid':jid
|
||||||
var occ = $(stanza).find('field[var="muc#roominfo_occupants"] value').text();
|
}))[0]);
|
||||||
var locked = $(stanza).find('feature[var="muc_passwordprotected"]').length;
|
}
|
||||||
var jid = $(stanza).attr('from');
|
$available_chatrooms.append(fragment);
|
||||||
var classes = locked && 'locked' || '';
|
$('input#show-rooms').show().siblings('img.spinner').remove();
|
||||||
delete this.rdict[jid];
|
|
||||||
this.$el.find('#available-chatrooms').append(
|
|
||||||
this.room_template({'name':name,
|
|
||||||
'desc':desc,
|
|
||||||
'occ':occ,
|
|
||||||
'jid':jid,
|
|
||||||
'classes': classes
|
|
||||||
}));
|
|
||||||
if (_.keys(this.rdict).length === 0) {
|
|
||||||
$('input#show-rooms').show().siblings('img.spinner').remove();
|
|
||||||
}
|
|
||||||
}, this));
|
|
||||||
}, this));
|
|
||||||
} else {
|
} else {
|
||||||
$available_chatrooms.html('<dt>No rooms on '+this.muc_domain+'</dt>');
|
$available_chatrooms.html('<dt>No rooms on '+this.muc_domain+'</dt>');
|
||||||
$('input#show-rooms').show().siblings('img.spinner').remove();
|
$('input#show-rooms').show().siblings('img.spinner').remove();
|
||||||
@ -780,6 +808,53 @@
|
|||||||
this.updateRoomsList();
|
this.updateRoomsList();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
showRoomInfo: function (ev) {
|
||||||
|
var target = ev.target,
|
||||||
|
$dd = $(target).parent('dd'),
|
||||||
|
$div = $dd.find('div.room-info');
|
||||||
|
if ($div.length) {
|
||||||
|
$div.remove();
|
||||||
|
} else {
|
||||||
|
$dd.append('<img class="spinner" src="images/spinner.gif"/>');
|
||||||
|
converse.connection.disco.info(
|
||||||
|
$(target).attr('data-room-jid'),
|
||||||
|
null,
|
||||||
|
$.proxy(function (stanza) {
|
||||||
|
var $stanza = $(stanza);
|
||||||
|
// All MUC features shown here: http://xmpp.org/registrar/disco-features.html
|
||||||
|
var desc = $stanza.find('field[var="muc#roominfo_description"] value').text();
|
||||||
|
var occ = $stanza.find('field[var="muc#roominfo_occupants"] value').text();
|
||||||
|
var hidden = $stanza.find('feature[var="muc_hidden"]').length;
|
||||||
|
var membersonly = $stanza.find('feature[var="muc_membersonly"]').length;
|
||||||
|
var moderated = $stanza.find('feature[var="muc_moderated"]').length;
|
||||||
|
var nonanonymous = $stanza.find('feature[var="muc_nonanonymous"]').length;
|
||||||
|
var open = $stanza.find('feature[var="muc_open"]').length;
|
||||||
|
var passwordprotected = $stanza.find('feature[var="muc_passwordprotected"]').length;
|
||||||
|
var persistent = $stanza.find('feature[var="muc_persistent"]').length;
|
||||||
|
var publicroom = $stanza.find('feature[var="muc_public"]').length;
|
||||||
|
var semianonymous = $stanza.find('feature[var="muc_semianonymous"]').length;
|
||||||
|
var temporary = $stanza.find('feature[var="muc_temporary"]').length;
|
||||||
|
var unmoderated = $stanza.find('feature[var="muc_unmoderated"]').length;
|
||||||
|
$dd.find('img.spinner').replaceWith(
|
||||||
|
this.room_description_template({
|
||||||
|
'desc':desc,
|
||||||
|
'occ':occ,
|
||||||
|
'hidden':hidden,
|
||||||
|
'membersonly':membersonly,
|
||||||
|
'moderated':moderated,
|
||||||
|
'nonanonymous':nonanonymous,
|
||||||
|
'open':open,
|
||||||
|
'passwordprotected':passwordprotected,
|
||||||
|
'persistent':persistent,
|
||||||
|
'publicroom': publicroom,
|
||||||
|
'semianonymous':semianonymous,
|
||||||
|
'temporary':temporary,
|
||||||
|
'unmoderated':unmoderated
|
||||||
|
}));
|
||||||
|
}, this));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
createChatRoom: function (ev) {
|
createChatRoom: function (ev) {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
var name, server, jid, $name, $server, errors;
|
var name, server, jid, $name, $server, errors;
|
||||||
@ -986,7 +1061,8 @@
|
|||||||
this.model.get('nick'),
|
this.model.get('nick'),
|
||||||
$.proxy(this.onChatRoomMessage, this),
|
$.proxy(this.onChatRoomMessage, this),
|
||||||
$.proxy(this.onChatRoomPresence, this),
|
$.proxy(this.onChatRoomPresence, this),
|
||||||
$.proxy(this.onChatRoomRoster, this));
|
$.proxy(this.onChatRoomRoster, this),
|
||||||
|
null);
|
||||||
|
|
||||||
this.model.messages.on('add', this.showMessage, this);
|
this.model.messages.on('add', this.showMessage, this);
|
||||||
this.model.on('destroy', function (model, response, options) {
|
this.model.on('destroy', function (model, response, options) {
|
||||||
@ -1004,22 +1080,84 @@
|
|||||||
|
|
||||||
onLeave: function () {},
|
onLeave: function () {},
|
||||||
|
|
||||||
|
showRoomConfigOptions: function (stanza) {
|
||||||
|
// FIXME: Show a proper configuration form
|
||||||
|
var $chat_content = this.$el.find('.chat-content'),
|
||||||
|
$stanza = $(stanza),
|
||||||
|
$fields = $stanza.find('field'),
|
||||||
|
title = $stanza.find('title').text(),
|
||||||
|
instructions = $stanza.find('instructions').text(),
|
||||||
|
i;
|
||||||
|
$chat_content.append(title);
|
||||||
|
$chat_content.append(instructions);
|
||||||
|
for (i=0; i<$fields.length; i++) {
|
||||||
|
$field = $($fields[i]);
|
||||||
|
$chat_content.append('<label>'+$field.attr('label')+'</label>');
|
||||||
|
// $chat_content.append('<input type="text" name=">'+$field.attr('label')+'</label>');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
onChatRoomPresence: function (presence, room) {
|
onChatRoomPresence: function (presence, room) {
|
||||||
var nick = room.nick,
|
var nick = room.nick,
|
||||||
$presence = $(presence),
|
$presence = $(presence),
|
||||||
from = $presence.attr('from');
|
from = $presence.attr('from'), item;
|
||||||
if ($presence.attr('type') !== 'error') {
|
if ($presence.attr('type') !== 'error') {
|
||||||
|
if ($presence.find("status[code='201']").length) {
|
||||||
|
// This is a new chatroom. We create an instant
|
||||||
|
// chatroom, and let the user manually set any
|
||||||
|
// configuration setting. (2nd part is TODO)
|
||||||
|
converse.connection.muc.createInstantRoom(room.name);
|
||||||
|
/* TODO: Find a place for this code (it configures a
|
||||||
|
* newly created chatroom).
|
||||||
|
* -------------------------------------------------
|
||||||
|
$item = $presence.find('item');
|
||||||
|
if ($item.length) {
|
||||||
|
if ($item.attr('affiliation') == 'owner') {
|
||||||
|
if (false) {
|
||||||
|
} else {
|
||||||
|
converse.connection.muc.configure(
|
||||||
|
room.name,
|
||||||
|
$.proxy(this.showRoomConfigOptions, this)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
||||||
// check for status 110 to see if it's our own presence
|
// check for status 110 to see if it's our own presence
|
||||||
if ($presence.find("status[code='110']").length) {
|
if ($presence.find("status[code='110']").length) {
|
||||||
// check if server changed our nick
|
|
||||||
if ($presence.find("status[code='210']").length) {
|
if ($presence.find("status[code='210']").length) {
|
||||||
|
// check if server changed our nick
|
||||||
this.model.set({'nick': Strophe.getResourceFromJid(from)});
|
this.model.set({'nick': Strophe.getResourceFromJid(from)});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
var error = $presence.find('error');
|
var $error = $presence.find('error'),
|
||||||
if ($(error).attr('type') == 'auth') {
|
$chat_content = this.$el.find('.chat-content');
|
||||||
this.$el.find('.chat-content').append('Sorry, this chatroom is restricted');
|
if ($error.attr('type') == 'auth') {
|
||||||
|
if ($error.find('not-authorized').length) {
|
||||||
|
$chat_content.append('This chatroom requires a password');
|
||||||
|
} else if ($error.find('registration-required').length) {
|
||||||
|
$chat_content.append('You are not on the member list of this room');
|
||||||
|
} else if ($error.find('forbidden').length) {
|
||||||
|
$chat_content.append('You have been banned from this room');
|
||||||
|
}
|
||||||
|
} else if ($error.attr('type') == 'modify') {
|
||||||
|
if ($error.find('jid-malformed').length) {
|
||||||
|
$chat_content.append('No nickname was specified');
|
||||||
|
}
|
||||||
|
} else if ($error.attr('type') == 'cancel') {
|
||||||
|
if ($error.find('not-allowed').length) {
|
||||||
|
$chat_content.append('You are not allowed to create new rooms');
|
||||||
|
} else if ($error.find('not-acceptable').length) {
|
||||||
|
$chat_content.append("Your nickname doesn't conform to the room's policies");
|
||||||
|
} else if ($error.find('conflict').length) {
|
||||||
|
$chat_content.append("Your nickname is already taken");
|
||||||
|
} else if ($error.find('item-not-found').length) {
|
||||||
|
$chat_content.append("This room does not (yet) exist");
|
||||||
|
} else if ($error.find('service-unavailable').length) {
|
||||||
|
$chat_content.append("This room has reached it's maximum number of occupants");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
@ -1108,16 +1246,32 @@
|
|||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
occupant_template: _.template(
|
||||||
|
'<li class="{{role}}" '+
|
||||||
|
'{[ if (role === "moderator") { ]}' +
|
||||||
|
'title="This user is a moderator"' +
|
||||||
|
'{[ } ]}'+
|
||||||
|
'{[ if (role === "participant") { ]}' +
|
||||||
|
'title="This user can send messages in this room"' +
|
||||||
|
'{[ } ]}'+
|
||||||
|
'{[ if (role === "visitor") { ]}' +
|
||||||
|
'title="This user can NOT send messages in this room"' +
|
||||||
|
'{[ } ]}'+
|
||||||
|
'>{{nick}}</li>'
|
||||||
|
),
|
||||||
|
|
||||||
onChatRoomRoster: function (roster, room) {
|
onChatRoomRoster: function (roster, room) {
|
||||||
// underscore size is needed because roster is an object
|
|
||||||
var controlboxview = converse.chatboxesview.views.controlbox,
|
var controlboxview = converse.chatboxesview.views.controlbox,
|
||||||
roster_size = _.size(roster),
|
roster_size = _.size(roster),
|
||||||
$participant_list = this.$el.find('.participant-list'),
|
$participant_list = this.$el.find('.participant-list'),
|
||||||
participants = [],
|
participants = [], keys = _.keys(roster), i;
|
||||||
i;
|
|
||||||
this.$el.find('.participant-list').empty();
|
this.$el.find('.participant-list').empty();
|
||||||
for (i=0; i<roster_size; i++) {
|
for (i=0; i<roster_size; i++) {
|
||||||
participants.push('<li>' + Strophe.unescapeNode(_.keys(roster)[i]) + '</li>');
|
participants.push(
|
||||||
|
this.occupant_template({
|
||||||
|
role: roster[keys[i]].role,
|
||||||
|
nick: Strophe.unescapeNode(keys[i])
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
$participant_list.append(participants.join(""));
|
$participant_list.append(participants.join(""));
|
||||||
return true;
|
return true;
|
||||||
@ -1344,7 +1498,6 @@
|
|||||||
this.$el.addClass('current-xmpp-contact');
|
this.$el.addClass('current-xmpp-contact');
|
||||||
this.$el.html(this.template(item.toJSON()));
|
this.$el.html(this.template(item.toJSON()));
|
||||||
}
|
}
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -1802,7 +1955,6 @@
|
|||||||
converse.connection.send($pres().c('show').t(this.get('status')).up().c('status').t(status_message));
|
converse.connection.send($pres().c('show').t(this.get('status')).up().c('status').t(status_message));
|
||||||
this.save({'status_message': status_message});
|
this.save({'status_message': status_message});
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
converse.XMPPStatusView = Backbone.View.extend({
|
converse.XMPPStatusView = Backbone.View.extend({
|
||||||
@ -1834,7 +1986,6 @@
|
|||||||
'<a class="change-xmpp-status-message" href="#" Title="Click here to write a custom status message"></a>' +
|
'<a class="change-xmpp-status-message" href="#" Title="Click here to write a custom status message"></a>' +
|
||||||
'</div>'),
|
'</div>'),
|
||||||
|
|
||||||
|
|
||||||
renderStatusChangeForm: function (ev) {
|
renderStatusChangeForm: function (ev) {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
var status_message = this.model.get('status') || 'offline';
|
var status_message = this.model.get('status') || 'offline';
|
||||||
@ -1937,6 +2088,7 @@
|
|||||||
* This collection stores Feature Models, representing features
|
* This collection stores Feature Models, representing features
|
||||||
* provided by available XMPP entities (e.g. servers)
|
* provided by available XMPP entities (e.g. servers)
|
||||||
* See XEP-0030 for more details: http://xmpp.org/extensions/xep-0030.html
|
* See XEP-0030 for more details: http://xmpp.org/extensions/xep-0030.html
|
||||||
|
* All features are shown here: http://xmpp.org/registrar/disco-features.html
|
||||||
*/
|
*/
|
||||||
model: converse.Feature,
|
model: converse.Feature,
|
||||||
initialize: function () {
|
initialize: function () {
|
||||||
@ -2114,8 +2266,8 @@
|
|||||||
|
|
||||||
converse.onConnected = function (connection) {
|
converse.onConnected = function (connection) {
|
||||||
this.connection = connection;
|
this.connection = connection;
|
||||||
// this.connection.xmlInput = function (body) { console.log(body); };
|
this.connection.xmlInput = function (body) { console.log(body); };
|
||||||
// this.connection.xmlOutput = function (body) { console.log(body); };
|
this.connection.xmlOutput = function (body) { console.log(body); };
|
||||||
this.bare_jid = Strophe.getBareJidFromJid(this.connection.jid);
|
this.bare_jid = Strophe.getBareJidFromJid(this.connection.jid);
|
||||||
this.domain = Strophe.getDomainFromJid(this.connection.jid);
|
this.domain = Strophe.getDomainFromJid(this.connection.jid);
|
||||||
this.features = new this.Features();
|
this.features = new this.Features();
|
||||||
|
43
mock.js
Normal file
43
mock.js
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
(function (root, factory) {
|
||||||
|
define("mock",
|
||||||
|
['converse'],
|
||||||
|
function() {
|
||||||
|
return factory();
|
||||||
|
});
|
||||||
|
}(this, function (converse) {
|
||||||
|
var mock_connection = {
|
||||||
|
'muc': {
|
||||||
|
'listRooms': function () {},
|
||||||
|
'join': function () {},
|
||||||
|
'leave': function () {}
|
||||||
|
},
|
||||||
|
'jid': 'dummy@localhost',
|
||||||
|
'addHandler': function (handler, ns, name, type, id, from, options) {
|
||||||
|
return function () {};
|
||||||
|
},
|
||||||
|
'send': function () {},
|
||||||
|
'roster': {
|
||||||
|
'add': function () {},
|
||||||
|
'authorize': function () {},
|
||||||
|
'unauthorize': function () {},
|
||||||
|
'get': function () {},
|
||||||
|
'subscribe': function () {},
|
||||||
|
'registerCallback': function () {}
|
||||||
|
},
|
||||||
|
'vcard': {
|
||||||
|
'get': function (callback, jid) {
|
||||||
|
var name = jid.split('@')[0].replace('.', ' ').split(' ');
|
||||||
|
var firstname = name[0].charAt(0).toUpperCase()+name[0].slice(1);
|
||||||
|
var lastname = name[1].charAt(0).toUpperCase()+name[1].slice(1);
|
||||||
|
var fullname = firstname+' '+lastname;
|
||||||
|
var vcard = $iq().c('vCard').c('FN').t(fullname);
|
||||||
|
callback(vcard.tree());
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'disco': {
|
||||||
|
'info': function () {},
|
||||||
|
'items': function () {}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return mock_connection;
|
||||||
|
}));
|
239
spec/ChatRoomSpec.js
Normal file
239
spec/ChatRoomSpec.js
Normal file
@ -0,0 +1,239 @@
|
|||||||
|
(function (root, factory) {
|
||||||
|
define([
|
||||||
|
"converse",
|
||||||
|
"mock"
|
||||||
|
], function (converse, mock_connection) {
|
||||||
|
return factory(converse, mock_connection);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
} (this, function (converse, mock_connection) {
|
||||||
|
return describe("ChatRooms", $.proxy(function() {
|
||||||
|
var chatroom_names = [
|
||||||
|
'Dyon van de Wege', 'Thomas Kalb', 'Dirk Theissen', 'Felix Hofmann', 'Ka Lek', 'Anne Ebersbacher'
|
||||||
|
];
|
||||||
|
describe("A Chat Room", $.proxy(function () {
|
||||||
|
beforeEach($.proxy(function () {
|
||||||
|
if (!$("div#controlbox").is(':visible')) {
|
||||||
|
$('.toggle-online-users').click();
|
||||||
|
}
|
||||||
|
var roomspanel = this.chatboxesview.views.controlbox.roomspanel;
|
||||||
|
var $input = roomspanel.$el.find('input.new-chatroom-name');
|
||||||
|
var $server = roomspanel.$el.find('input.new-chatroom-server');
|
||||||
|
$input.val('lounge');
|
||||||
|
$server.val('muc.localhost');
|
||||||
|
roomspanel.$el.find('form').submit();
|
||||||
|
$('.toggle-online-users').click();
|
||||||
|
}, converse));
|
||||||
|
|
||||||
|
it("shows users currently present in the room", $.proxy(function () {
|
||||||
|
var chatroomview = this.chatboxesview.views['lounge@muc.localhost'];
|
||||||
|
var $participant_list = chatroomview.$el.find('.participant-list');
|
||||||
|
var roster = {}, room = {}, i;
|
||||||
|
|
||||||
|
for (i=0; i<chatroom_names.length-1; i++) {
|
||||||
|
roster[chatroom_names[i]] = {};
|
||||||
|
chatroomview.onChatRoomRoster(roster, room);
|
||||||
|
expect($participant_list.find('li').length).toBe(1+i);
|
||||||
|
expect($($participant_list.find('li')[i]).text()).toBe(chatroom_names[i]);
|
||||||
|
}
|
||||||
|
roster[converse.bare_jid] = {};
|
||||||
|
chatroomview.onChatRoomRoster(roster, room);
|
||||||
|
}, converse));
|
||||||
|
|
||||||
|
it("indicates moderators by means of a special css class and tooltip", $.proxy(function () {
|
||||||
|
var chatroomview = this.chatboxesview.views['lounge@muc.localhost'];
|
||||||
|
var $participant_list = chatroomview.$el.find('.participant-list');
|
||||||
|
var roster = {}, idx = chatroom_names.length-1;
|
||||||
|
roster[chatroom_names[idx]] = {};
|
||||||
|
roster[chatroom_names[idx]].role = 'moderator';
|
||||||
|
chatroomview.onChatRoomRoster(roster, {});
|
||||||
|
occupant = $participant_list.find('li');
|
||||||
|
expect(occupant.length).toBe(1);
|
||||||
|
expect($(occupant).text()).toBe(chatroom_names[idx]);
|
||||||
|
expect($(occupant).attr('class')).toBe('moderator');
|
||||||
|
expect($(occupant).attr('title')).toBe('This user is a moderator');
|
||||||
|
}, converse));
|
||||||
|
|
||||||
|
it("can be saved to, and retrieved from, localStorage", $.proxy(function () {
|
||||||
|
// We instantiate a new ChatBoxes collection, which by default
|
||||||
|
// will be empty.
|
||||||
|
var newchatboxes = new this.ChatBoxes();
|
||||||
|
expect(newchatboxes.length).toEqual(0);
|
||||||
|
// The chatboxes will then be fetched from localStorage inside the
|
||||||
|
// onConnected method
|
||||||
|
newchatboxes.onConnected();
|
||||||
|
expect(newchatboxes.length).toEqual(1);
|
||||||
|
// Check that the chatrooms retrieved from localStorage
|
||||||
|
// have the same attributes values as the original ones.
|
||||||
|
attrs = ['id', 'box_id', 'visible'];
|
||||||
|
for (i=0; i<attrs.length; i++) {
|
||||||
|
new_attrs = _.pluck(_.pluck(newchatboxes.models, 'attributes'), attrs[i]);
|
||||||
|
old_attrs = _.pluck(_.pluck(this.chatboxes.models, 'attributes'), attrs[i]);
|
||||||
|
expect(_.isEqual(new_attrs, old_attrs)).toEqual(true);
|
||||||
|
}
|
||||||
|
this.rosterview.render();
|
||||||
|
}, converse));
|
||||||
|
|
||||||
|
it("can be closed again by clicking a DOM element with class 'close-chatbox-button'", $.proxy(function () {
|
||||||
|
var view = this.chatboxesview.views['lounge@muc.localhost'], chatroom = view.model, $el;
|
||||||
|
spyOn(view, 'closeChat').andCallThrough();
|
||||||
|
spyOn(converse.connection.muc, 'leave');
|
||||||
|
view.delegateEvents(); // We need to rebind all events otherwise our spy won't be called
|
||||||
|
view.$el.find('.close-chatbox-button').click();
|
||||||
|
expect(view.closeChat).toHaveBeenCalled();
|
||||||
|
expect(converse.connection.muc.leave).toHaveBeenCalled();
|
||||||
|
}, converse));
|
||||||
|
}, converse));
|
||||||
|
|
||||||
|
describe("When attempting to enter a chatroom", $.proxy(function () {
|
||||||
|
beforeEach($.proxy(function () {
|
||||||
|
var roomspanel = this.chatboxesview.views.controlbox.roomspanel;
|
||||||
|
var $input = roomspanel.$el.find('input.new-chatroom-name');
|
||||||
|
var $server = roomspanel.$el.find('input.new-chatroom-server');
|
||||||
|
$input.val('problematic');
|
||||||
|
$server.val('muc.localhost');
|
||||||
|
roomspanel.$el.find('form').submit();
|
||||||
|
}, converse));
|
||||||
|
|
||||||
|
afterEach($.proxy(function () {
|
||||||
|
var view = this.chatboxesview.views['problematic@muc.localhost'];
|
||||||
|
view.closeChat();
|
||||||
|
}, converse));
|
||||||
|
|
||||||
|
it("will show an error message if the room requires a password", $.proxy(function () {
|
||||||
|
var presence = $pres().attrs({
|
||||||
|
from:'coven@chat.shakespeare.lit/thirdwitch',
|
||||||
|
id:'n13mt3l',
|
||||||
|
to:'hag66@shakespeare.lit/pda',
|
||||||
|
type:'error'})
|
||||||
|
.c('x').attrs({xmlns:'http://jabber.org/protocol/muc'}).up()
|
||||||
|
.c('error').attrs({by:'coven@chat.shakespeare.lit', type:'auth'})
|
||||||
|
.c('not-authorized').attrs({xmlns:'urn:ietf:params:xml:ns:xmpp-stanzas'}).nodeTree;
|
||||||
|
var view = this.chatboxesview.views['problematic@muc.localhost'];
|
||||||
|
view.onChatRoomPresence(presence, {'nick': 'dummy'});
|
||||||
|
var $chat_content = view.$el.find('.chat-content');
|
||||||
|
expect($chat_content.text()).toBe('This chatroom requires a password');
|
||||||
|
}, converse));
|
||||||
|
|
||||||
|
it("will show an error message if the room is members-only and the user not included", $.proxy(function () {
|
||||||
|
var presence = $pres().attrs({
|
||||||
|
from:'coven@chat.shakespeare.lit/thirdwitch',
|
||||||
|
id:'n13mt3l',
|
||||||
|
to:'hag66@shakespeare.lit/pda',
|
||||||
|
type:'error'})
|
||||||
|
.c('x').attrs({xmlns:'http://jabber.org/protocol/muc'}).up()
|
||||||
|
.c('error').attrs({by:'coven@chat.shakespeare.lit', type:'auth'})
|
||||||
|
.c('registration-required').attrs({xmlns:'urn:ietf:params:xml:ns:xmpp-stanzas'}).nodeTree;
|
||||||
|
var view = this.chatboxesview.views['problematic@muc.localhost'];
|
||||||
|
view.onChatRoomPresence(presence, {'nick': 'dummy'});
|
||||||
|
var $chat_content = view.$el.find('.chat-content');
|
||||||
|
expect($chat_content.text()).toBe('You are not on the member list of this room');
|
||||||
|
}, converse));
|
||||||
|
|
||||||
|
it("will show an error message if the user has been banned", $.proxy(function () {
|
||||||
|
var presence = $pres().attrs({
|
||||||
|
from:'coven@chat.shakespeare.lit/thirdwitch',
|
||||||
|
id:'n13mt3l',
|
||||||
|
to:'hag66@shakespeare.lit/pda',
|
||||||
|
type:'error'})
|
||||||
|
.c('x').attrs({xmlns:'http://jabber.org/protocol/muc'}).up()
|
||||||
|
.c('error').attrs({by:'coven@chat.shakespeare.lit', type:'auth'})
|
||||||
|
.c('forbidden').attrs({xmlns:'urn:ietf:params:xml:ns:xmpp-stanzas'}).nodeTree;
|
||||||
|
var view = this.chatboxesview.views['problematic@muc.localhost'];
|
||||||
|
view.onChatRoomPresence(presence, {'nick': 'dummy'});
|
||||||
|
var $chat_content = view.$el.find('.chat-content');
|
||||||
|
expect($chat_content.text()).toBe('You have been banned from this room');
|
||||||
|
}, converse));
|
||||||
|
|
||||||
|
it("will show an error message if no nickname was specified for the user", $.proxy(function () {
|
||||||
|
var presence = $pres().attrs({
|
||||||
|
from:'coven@chat.shakespeare.lit/thirdwitch',
|
||||||
|
id:'n13mt3l',
|
||||||
|
to:'hag66@shakespeare.lit/pda',
|
||||||
|
type:'error'})
|
||||||
|
.c('x').attrs({xmlns:'http://jabber.org/protocol/muc'}).up()
|
||||||
|
.c('error').attrs({by:'coven@chat.shakespeare.lit', type:'modify'})
|
||||||
|
.c('jid-malformed').attrs({xmlns:'urn:ietf:params:xml:ns:xmpp-stanzas'}).nodeTree;
|
||||||
|
var view = this.chatboxesview.views['problematic@muc.localhost'];
|
||||||
|
view.onChatRoomPresence(presence, {'nick': 'dummy'});
|
||||||
|
var $chat_content = view.$el.find('.chat-content');
|
||||||
|
expect($chat_content.text()).toBe('No nickname was specified');
|
||||||
|
}, converse));
|
||||||
|
|
||||||
|
it("will show an error message if the user is not allowed to have created the room", $.proxy(function () {
|
||||||
|
var presence = $pres().attrs({
|
||||||
|
from:'coven@chat.shakespeare.lit/thirdwitch',
|
||||||
|
id:'n13mt3l',
|
||||||
|
to:'hag66@shakespeare.lit/pda',
|
||||||
|
type:'error'})
|
||||||
|
.c('x').attrs({xmlns:'http://jabber.org/protocol/muc'}).up()
|
||||||
|
.c('error').attrs({by:'coven@chat.shakespeare.lit', type:'cancel'})
|
||||||
|
.c('not-allowed').attrs({xmlns:'urn:ietf:params:xml:ns:xmpp-stanzas'}).nodeTree;
|
||||||
|
var view = this.chatboxesview.views['problematic@muc.localhost'];
|
||||||
|
view.onChatRoomPresence(presence, {'nick': 'dummy'});
|
||||||
|
var $chat_content = view.$el.find('.chat-content');
|
||||||
|
expect($chat_content.text()).toBe('You are not allowed to create new rooms');
|
||||||
|
}, converse));
|
||||||
|
|
||||||
|
it("will show an error message if the user's nickname doesn't conform to room policy", $.proxy(function () {
|
||||||
|
var presence = $pres().attrs({
|
||||||
|
from:'coven@chat.shakespeare.lit/thirdwitch',
|
||||||
|
id:'n13mt3l',
|
||||||
|
to:'hag66@shakespeare.lit/pda',
|
||||||
|
type:'error'})
|
||||||
|
.c('x').attrs({xmlns:'http://jabber.org/protocol/muc'}).up()
|
||||||
|
.c('error').attrs({by:'coven@chat.shakespeare.lit', type:'cancel'})
|
||||||
|
.c('not-acceptable').attrs({xmlns:'urn:ietf:params:xml:ns:xmpp-stanzas'}).nodeTree;
|
||||||
|
var view = this.chatboxesview.views['problematic@muc.localhost'];
|
||||||
|
view.onChatRoomPresence(presence, {'nick': 'dummy'});
|
||||||
|
var $chat_content = view.$el.find('.chat-content');
|
||||||
|
expect($chat_content.text()).toBe("Your nickname doesn't conform to the room's policies");
|
||||||
|
}, converse));
|
||||||
|
|
||||||
|
it("will show an error message if the user's nickname is already taken", $.proxy(function () {
|
||||||
|
var presence = $pres().attrs({
|
||||||
|
from:'coven@chat.shakespeare.lit/thirdwitch',
|
||||||
|
id:'n13mt3l',
|
||||||
|
to:'hag66@shakespeare.lit/pda',
|
||||||
|
type:'error'})
|
||||||
|
.c('x').attrs({xmlns:'http://jabber.org/protocol/muc'}).up()
|
||||||
|
.c('error').attrs({by:'coven@chat.shakespeare.lit', type:'cancel'})
|
||||||
|
.c('conflict').attrs({xmlns:'urn:ietf:params:xml:ns:xmpp-stanzas'}).nodeTree;
|
||||||
|
var view = this.chatboxesview.views['problematic@muc.localhost'];
|
||||||
|
view.onChatRoomPresence(presence, {'nick': 'dummy'});
|
||||||
|
var $chat_content = view.$el.find('.chat-content');
|
||||||
|
expect($chat_content.text()).toBe("Your nickname is already taken");
|
||||||
|
}, converse));
|
||||||
|
|
||||||
|
it("will show an error message if the room doesn't yet exist", $.proxy(function () {
|
||||||
|
var presence = $pres().attrs({
|
||||||
|
from:'coven@chat.shakespeare.lit/thirdwitch',
|
||||||
|
id:'n13mt3l',
|
||||||
|
to:'hag66@shakespeare.lit/pda',
|
||||||
|
type:'error'})
|
||||||
|
.c('x').attrs({xmlns:'http://jabber.org/protocol/muc'}).up()
|
||||||
|
.c('error').attrs({by:'coven@chat.shakespeare.lit', type:'cancel'})
|
||||||
|
.c('item-not-found').attrs({xmlns:'urn:ietf:params:xml:ns:xmpp-stanzas'}).nodeTree;
|
||||||
|
var view = this.chatboxesview.views['problematic@muc.localhost'];
|
||||||
|
view.onChatRoomPresence(presence, {'nick': 'dummy'});
|
||||||
|
var $chat_content = view.$el.find('.chat-content');
|
||||||
|
expect($chat_content.text()).toBe("This room does not (yet) exist");
|
||||||
|
}, converse));
|
||||||
|
|
||||||
|
it("will show an error message if the room has reached it's maximum number of occupants", $.proxy(function () {
|
||||||
|
var presence = $pres().attrs({
|
||||||
|
from:'coven@chat.shakespeare.lit/thirdwitch',
|
||||||
|
id:'n13mt3l',
|
||||||
|
to:'hag66@shakespeare.lit/pda',
|
||||||
|
type:'error'})
|
||||||
|
.c('x').attrs({xmlns:'http://jabber.org/protocol/muc'}).up()
|
||||||
|
.c('error').attrs({by:'coven@chat.shakespeare.lit', type:'cancel'})
|
||||||
|
.c('service-unavailable').attrs({xmlns:'urn:ietf:params:xml:ns:xmpp-stanzas'}).nodeTree;
|
||||||
|
var view = this.chatboxesview.views['problematic@muc.localhost'];
|
||||||
|
view.onChatRoomPresence(presence, {'nick': 'dummy'});
|
||||||
|
var $chat_content = view.$el.find('.chat-content');
|
||||||
|
expect($chat_content.text()).toBe("This room has reached it's maximum number of occupants");
|
||||||
|
}, converse));
|
||||||
|
}, converse));
|
||||||
|
}, converse));
|
||||||
|
}));
|
116
spec/MainSpec.js
116
spec/MainSpec.js
@ -1,12 +1,12 @@
|
|||||||
(function (root, factory) {
|
(function (root, factory) {
|
||||||
define([
|
define([
|
||||||
"converse"
|
"converse",
|
||||||
], function (converse) {
|
"mock"
|
||||||
return factory(converse);
|
], function (converse, mock_connection) {
|
||||||
|
return factory(converse, mock_connection);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
} (this, function (converse) {
|
} (this, function (converse, mock_connection) {
|
||||||
|
|
||||||
return describe("Converse.js", $.proxy(function() {
|
return describe("Converse.js", $.proxy(function() {
|
||||||
// Names from http://www.fakenamegenerator.com/
|
// Names from http://www.fakenamegenerator.com/
|
||||||
var req_names = [
|
var req_names = [
|
||||||
@ -20,56 +20,7 @@
|
|||||||
'Robin Schook', 'Marcel Eberhardt', 'Simone Brauer', 'Asmaa Haakman', 'Felix Amsel',
|
'Robin Schook', 'Marcel Eberhardt', 'Simone Brauer', 'Asmaa Haakman', 'Felix Amsel',
|
||||||
'Lena Grunewald', 'Laura Grunewald', 'Mandy Seiler', 'Sven Bosch', 'Nuriye Cuypers'
|
'Lena Grunewald', 'Laura Grunewald', 'Mandy Seiler', 'Sven Bosch', 'Nuriye Cuypers'
|
||||||
];
|
];
|
||||||
var chatroom_names = [
|
|
||||||
'Dyon van de Wege', 'Thomas Kalb', 'Dirk Theissen', 'Felix Hofmann', 'Ka Lek', 'Anne Ebersbacher'
|
|
||||||
];
|
|
||||||
var num_contacts = req_names.length + pend_names.length + cur_names.length;
|
var num_contacts = req_names.length + pend_names.length + cur_names.length;
|
||||||
mock_connection = {
|
|
||||||
'muc': {
|
|
||||||
'listRooms': function () {},
|
|
||||||
'join': function () {},
|
|
||||||
'leave': function () {}
|
|
||||||
},
|
|
||||||
'jid': 'dummy@localhost',
|
|
||||||
'addHandler': function (handler, ns, name, type, id, from, options) {
|
|
||||||
return function () {};
|
|
||||||
},
|
|
||||||
'send': function () {},
|
|
||||||
'roster': {
|
|
||||||
'add': function () {},
|
|
||||||
'authorize': function () {},
|
|
||||||
'unauthorize': function () {},
|
|
||||||
'get': function () {},
|
|
||||||
'subscribe': function () {},
|
|
||||||
'registerCallback': function () {}
|
|
||||||
},
|
|
||||||
'vcard': {
|
|
||||||
'get': function (callback, jid) {
|
|
||||||
var name = jid.split('@')[0].replace('.', ' ').split(' ');
|
|
||||||
var firstname = name[0].charAt(0).toUpperCase()+name[0].slice(1);
|
|
||||||
var lastname = name[1].charAt(0).toUpperCase()+name[1].slice(1);
|
|
||||||
var fullname = firstname+' '+lastname;
|
|
||||||
var vcard = $iq().c('vCard').c('FN').t(fullname);
|
|
||||||
callback(vcard.tree());
|
|
||||||
}
|
|
||||||
},
|
|
||||||
'disco': {
|
|
||||||
'info': function () {},
|
|
||||||
'items': function () {}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Clear localStorage
|
|
||||||
window.localStorage.clear();
|
|
||||||
this.initialize({
|
|
||||||
prebind: false,
|
|
||||||
xhr_user_search: false,
|
|
||||||
auto_subscribe: false,
|
|
||||||
animate: false
|
|
||||||
});
|
|
||||||
this.onConnected(mock_connection);
|
|
||||||
|
|
||||||
// Variable declarations for specs
|
|
||||||
var open_controlbox;
|
var open_controlbox;
|
||||||
|
|
||||||
describe("The Control Box", $.proxy(function () {
|
describe("The Control Box", $.proxy(function () {
|
||||||
@ -82,8 +33,10 @@
|
|||||||
// open yet.
|
// open yet.
|
||||||
expect($("div#controlbox").is(':visible')).toBe(false);
|
expect($("div#controlbox").is(':visible')).toBe(false);
|
||||||
spyOn(this, 'toggleControlBox').andCallThrough();
|
spyOn(this, 'toggleControlBox').andCallThrough();
|
||||||
|
spyOn(this, 'showControlBox').andCallThrough();
|
||||||
$('.toggle-online-users').click();
|
$('.toggle-online-users').click();
|
||||||
expect(this.toggleControlBox).toHaveBeenCalled();
|
expect(this.toggleControlBox).toHaveBeenCalled();
|
||||||
|
expect(this.showControlBox).toHaveBeenCalled();
|
||||||
expect($("div#controlbox").is(':visible')).toBe(true);
|
expect($("div#controlbox").is(':visible')).toBe(true);
|
||||||
}, converse);
|
}, converse);
|
||||||
it("can be opened by clicking a DOM element with class 'toggle-online-users'", open_controlbox);
|
it("can be opened by clicking a DOM element with class 'toggle-online-users'", open_controlbox);
|
||||||
@ -577,8 +530,13 @@
|
|||||||
|
|
||||||
it("is cleared when the window is focused", $.proxy(function () {
|
it("is cleared when the window is focused", $.proxy(function () {
|
||||||
spyOn(converse, 'clearMsgCounter').andCallThrough();
|
spyOn(converse, 'clearMsgCounter').andCallThrough();
|
||||||
$(window).trigger('focus');
|
runs(function () {
|
||||||
expect(converse.clearMsgCounter).toHaveBeenCalled();
|
$(window).trigger('focus');
|
||||||
|
});
|
||||||
|
waits(50);
|
||||||
|
runs(function () {
|
||||||
|
expect(converse.clearMsgCounter).toHaveBeenCalled();
|
||||||
|
});
|
||||||
}, converse));
|
}, converse));
|
||||||
|
|
||||||
it("is not incremented when the message is received and the window is focused", $.proxy(function () {
|
it("is not incremented when the message is received and the window is focused", $.proxy(function () {
|
||||||
@ -667,51 +625,5 @@
|
|||||||
}, converse));
|
}, converse));
|
||||||
}, converse));
|
}, converse));
|
||||||
}, converse));
|
}, converse));
|
||||||
|
|
||||||
describe("A Chat Room", $.proxy(function () {
|
|
||||||
it("shows users currently present in the room", $.proxy(function () {
|
|
||||||
var chatroomview = this.chatboxesview.views['lounge@muc.localhost'];
|
|
||||||
var $participant_list = chatroomview.$el.find('.participant-list');
|
|
||||||
var roster = {}, room = {}, i;
|
|
||||||
for (i=0; i<chatroom_names.length; i++) {
|
|
||||||
roster[chatroom_names[i]] = {};
|
|
||||||
chatroomview.onChatRoomRoster(roster, room);
|
|
||||||
expect($participant_list.find('li').length).toBe(1+i);
|
|
||||||
expect($($participant_list.find('li')[i]).text()).toBe(chatroom_names[i]);
|
|
||||||
}
|
|
||||||
roster[converse.bare_jid] = {};
|
|
||||||
chatroomview.onChatRoomRoster(roster, room);
|
|
||||||
}, converse));
|
|
||||||
|
|
||||||
it("can be saved to, and retrieved from, localStorage", $.proxy(function () {
|
|
||||||
// We instantiate a new ChatBoxes collection, which by default
|
|
||||||
// will be empty.
|
|
||||||
var newchatboxes = new this.ChatBoxes();
|
|
||||||
expect(newchatboxes.length).toEqual(0);
|
|
||||||
// The chatboxes will then be fetched from localStorage inside the
|
|
||||||
// onConnected method
|
|
||||||
newchatboxes.onConnected();
|
|
||||||
expect(newchatboxes.length).toEqual(2); // controlbox is also included
|
|
||||||
// Check that the chatrooms retrieved from localStorage
|
|
||||||
// have the same attributes values as the original ones.
|
|
||||||
attrs = ['id', 'box_id', 'visible'];
|
|
||||||
for (i=0; i<attrs.length; i++) {
|
|
||||||
new_attrs = _.pluck(_.pluck(newchatboxes.models, 'attributes'), attrs[i]);
|
|
||||||
old_attrs = _.pluck(_.pluck(this.chatboxes.models, 'attributes'), attrs[i]);
|
|
||||||
expect(_.isEqual(new_attrs, old_attrs)).toEqual(true);
|
|
||||||
}
|
|
||||||
this.rosterview.render();
|
|
||||||
}, converse));
|
|
||||||
|
|
||||||
it("can be closed again by clicking a DOM element with class 'close-chatbox-button'", $.proxy(function () {
|
|
||||||
var view = this.chatboxesview.views['lounge@muc.localhost'], chatroom = view.model, $el;
|
|
||||||
spyOn(view, 'closeChat').andCallThrough();
|
|
||||||
spyOn(converse.connection.muc, 'leave');
|
|
||||||
view.delegateEvents(); // We need to rebind all events otherwise our spy won't be called
|
|
||||||
view.$el.find('.close-chatbox-button').click();
|
|
||||||
expect(view.closeChat).toHaveBeenCalled();
|
|
||||||
expect(converse.connection.muc.leave).toHaveBeenCalled();
|
|
||||||
}, converse));
|
|
||||||
}, converse));
|
|
||||||
}, converse));
|
}, converse));
|
||||||
}));
|
}));
|
||||||
|
@ -1,16 +1,21 @@
|
|||||||
require(["jquery", "spec/MainSpec"], function($) {
|
require(["jquery", "converse", "mock", "spec/MainSpec", "spec/ChatRoomSpec"], function($, converse, mock_connection) {
|
||||||
|
// Set up converse.js
|
||||||
$(function($) {
|
window.localStorage.clear();
|
||||||
var jasmineEnv = jasmine.getEnv();
|
converse.initialize({
|
||||||
jasmineEnv.updateInterval = 500;
|
prebind: false,
|
||||||
|
xhr_user_search: false,
|
||||||
var htmlReporter = new jasmine.HtmlReporter();
|
auto_subscribe: false,
|
||||||
|
animate: false
|
||||||
jasmineEnv.addReporter(htmlReporter);
|
|
||||||
|
|
||||||
jasmineEnv.specFilter = function(spec) {
|
|
||||||
return htmlReporter.specFilter(spec);
|
|
||||||
};
|
|
||||||
jasmineEnv.execute();
|
|
||||||
});
|
});
|
||||||
|
converse.onConnected(mock_connection);
|
||||||
|
|
||||||
|
// Jasmine stuff
|
||||||
|
var jasmineEnv = jasmine.getEnv();
|
||||||
|
jasmineEnv.updateInterval = 50;
|
||||||
|
var htmlReporter = new jasmine.HtmlReporter();
|
||||||
|
jasmineEnv.addReporter(htmlReporter);
|
||||||
|
jasmineEnv.specFilter = function(spec) {
|
||||||
|
return htmlReporter.specFilter(spec);
|
||||||
|
};
|
||||||
|
jasmineEnv.execute();
|
||||||
});
|
});
|
||||||
|
Loading…
Reference in New Issue
Block a user