Merge branch 'live-filter'

Conflicts:
	builds/converse-no-locales-no-otr.min.js
	builds/converse-no-otr.min.js
	builds/converse.min.js
	builds/converse.website-no-otr.min.js
	builds/converse.website.min.js
This commit is contained in:
JC Brand 2014-08-11 21:57:23 +02:00
commit bf837a921e
35 changed files with 693 additions and 407 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -292,6 +292,9 @@
var HEADER_REQUESTING_CONTACTS = __('Contact requests');
var HEADER_UNGROUPED = __('Ungrouped');
var LABEL_CONTACTS = __('Contacts');
var LABEL_GROUPS = __('Groups');
var HEADER_WEIGHTS = {};
HEADER_WEIGHTS[HEADER_CURRENT_CONTACTS] = 0;
HEADER_WEIGHTS[HEADER_UNGROUPED] = 1;
@ -307,8 +310,7 @@
// Module-level functions
// ----------------------
this.giveFeedback = function (message, klass) {
$('.conn-feedback').text(message);
$('.conn-feedback').attr('class', 'conn-feedback');
$('.conn-feedback').attr('class', 'conn-feedback').text(message);
if (klass) {
$('.conn-feedback').addClass(klass);
}
@ -1497,7 +1499,7 @@
label_away: __('Away'),
label_offline: __('Offline')
});
this.$tabs.append(converse.templates.contacts_tab({label_contacts: __('Contacts')}));
this.$tabs.append(converse.templates.contacts_tab({label_contacts: LABEL_CONTACTS}));
if (converse.xhr_user_search) {
markup = converse.templates.search_contact({
label_contact_name: __('Contact name'),
@ -1517,9 +1519,9 @@
});
}
this.$el.html(widgets);
this.$el.find('.search-xmpp ul').append(markup);
this.$el.append(converse.rosterview.$el);
converse.rosterview.update(); // Will render live filter if needed.
return this;
},
@ -3245,7 +3247,8 @@
initialize: function () {
this.model.contacts.on("add", this.addContact, this);
this.model.contacts.on("change:chat_status", function (contact) {
// This might be optimized by instead of first sorting, finding the correct position in positionContact
// This might be optimized by instead of first sorting,
// finding the correct position in positionContact
this.model.contacts.sort();
this.positionContact(contact).render();
}, this);
@ -3282,23 +3285,73 @@
return view;
},
hide: function () {
this.$el.nextUntil('dt').addBack().hide();
},
filter: function (q) {
/* Filter the group's contacts based on the query "q".
* The query is matched against the contact's full name.
* If all contacts are filtered out (i.e. hidden), then the
* group must be filtered out as well.
*/
var matches, rejects;
var predicate = function (item) {
return item.get('fullname').toLowerCase().indexOf(q) === -1;
};
if (q.length === 0) {
if (this.model.get('state') === OPENED) {
this.model.contacts.each($.proxy(function (item) {
if (!(converse.show_only_online_users && item.get('chat_status') === 'online')) {
this.get(item.get('id')).$el.show();
}
}, this));
}
this.showIfInvisible();
} else {
q = q.toLowerCase();
matches = this.model.contacts.filter(predicate);
if (matches.length === this.model.contacts.length) { // hide the whole group
this.hide();
} else {
_.each(matches, $.proxy(function (item) {
this.get(item.get('id')).$el.hide();
}, this));
_.each(this.model.contacts.reject(predicate), $.proxy(function (item) {
this.get(item.get('id')).$el.show();
}, this));
this.showIfInvisible();
}
}
},
showIfInvisible: function () {
if (!this.$el.is(':visible')) {
this.$el.show();
}
},
toggle: function (ev) {
if (ev && ev.preventDefault) { ev.preventDefault(); }
var $el = $(ev.target);
this.$el.nextUntil('dt').slideToggle();
if ($el.hasClass("icon-opened")) {
this.$el.nextUntil('dt').slideUp();
this.model.save({state: CLOSED});
$el.removeClass("icon-opened").addClass("icon-closed");
} else {
$el.removeClass("icon-closed").addClass("icon-opened");
this.model.save({state: OPENED});
this.filter(
converse.rosterview.$('.roster-filter').val(),
converse.rosterview.$('.filter-type').val()
);
}
},
addContact: function (contact) {
var view = new converse.RosterContactView({model: contact});
this.add(contact.get('id'), view);
var view = this.positionContact(contact).render();
view = this.positionContact(contact).render();
if (this.model.get('state') === CLOSED) {
view.$el.hide();
}
@ -3347,8 +3400,14 @@
});
this.RosterView = Backbone.Overview.extend({
tagName: 'dl',
tagName: 'div',
id: 'converse-roster',
events: {
"keydown .roster-filter": "liveFilter",
"click .onX": "clearFilter",
"mousemove .x": "togglePointer",
"change .filter-type": "changeFilterType"
},
initialize: function () {
this.registerRosterHandler();
@ -3369,7 +3428,80 @@
},
render: function () {
this.$el.empty();
this.$el.html(converse.templates.roster({
placeholder: __('Type to filter'),
label_contacts: LABEL_CONTACTS,
label_groups: LABEL_GROUPS
}));
return this;
},
changeFilterType: function (ev) {
if (ev && ev.preventDefault) { ev.preventDefault(); }
this.clearFilter();
this.filter(
this.$('.roster-filter').val(),
ev.target.value
);
},
tog: function (v) {
return v?'addClass':'removeClass';
},
togglePointer: function (ev) {
if (ev && ev.preventDefault) { ev.preventDefault(); }
var el = ev.target;
$(el)[this.tog(el.offsetWidth-18 < ev.clientX-el.getBoundingClientRect().left)]('onX');
},
filter: function (query, type) {
var matches;
query = query.toLowerCase();
if (type === 'groups') {
matches = _.filter(this.getAll(), function (view) {
return view.model.get('name').toLowerCase().indexOf(query) === -1;
});
_.each(matches, function (view) {
view.hide();
});
} else {
_.each(this.getAll(), function (view) {
view.filter(query, type);
});
}
},
liveFilter: _.debounce(function (ev) {
if (ev && ev.preventDefault) { ev.preventDefault(); }
var q = ev.target.value;
var t = this.$('.filter-type').val();
$(ev.target)[this.tog(q)]('x');
this.filter(q, t);
}, 500),
clearFilter: function (ev) {
if (ev && ev.preventDefault) {
ev.preventDefault();
$(ev.target).removeClass('x onX').val('');
}
this.filter('');
},
showHideFilter: function () {
var $filter = this.$('.roster-filter');
var visible = $filter.is(':visible');
if (visible && $filter.val().length > 0) {
// Don't hide if user is currently filtering.
return;
}
if (this.$('.roster-contacts').hasScrollBar()) {
if (!visible) {
$filter.show();
}
} else {
$filter.hide();
}
return this;
},
@ -3380,7 +3512,7 @@
if (!$count.is(':visible')) {
$count.show();
}
return this;
return this.showHideFilter();
},
reset: function () {
@ -3457,13 +3589,13 @@
*/
model.sort();
model.each($.proxy(function (group, idx) {
var view = this.get(group.get('name'))
var view = this.get(group.get('name'));
if (!view) {
view = new converse.RosterGroupView({model: group});
this.add(group.get('name'), view.render());
}
if (idx === 0) {
this.$el.append(view.$el);
this.$('.roster-contacts').append(view.$el);
} else {
this.appendGroup(view);
}
@ -3476,7 +3608,7 @@
*/
var index = this.model.indexOf(view.model);
if (index === 0) {
this.$el.prepend(view.$el);
this.$('.roster-contacts').prepend(view.$el);
} else if (index == (this.model.length-1)) {
this.appendGroup(view);
} else {

View File

@ -754,7 +754,7 @@ dl.add-converse-contact {
clear: right;
height: 22px;
width: 12px;
padding: 0px 5px 0 0;
padding: 0px 15px 0 0;
color: #4f4f4f;
}
#conversejs ul#found-users {
@ -817,7 +817,7 @@ dl.add-converse-contact {
top: 1px;
}
#conversejs .controlbox-pane dt {
padding: 3px;
padding-bottom: 3px;
}
#conversejs .chatroom-form-container {
height: 100%;
@ -872,24 +872,52 @@ dl.add-converse-contact {
white-space: nowrap;
text-overflow: ellipsis;
}
#conversejs #converse-roster span.req-contact-name {
#converse-roster span.req-contact-name {
width: 65%;
}
#conversejs #converse-roster span.pending-contact-name,
#conversejs #converse-roster a.open-chat {
#converse-roster span.pending-contact-name,
#converse-roster a.open-chat {
width: 80%;
}
#converse-roster dd span {
padding: 0 5px 0 0;
}
#converse-roster {
overflow-y: auto;
overflow-x: hidden;
width: 100%;
position: relative;
margin: 0.5em 0 0 0;
height: 254px;
height: calc(100% - 70px);
height: 194px;
height: calc(100% - 25px);
overflow-x: hidden;
}
#converse-roster .roster-filter {
padding: 0;
margin: 0 0 5px 0.5em;
width: 111px;
height: 20px;
background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAcAAAAHCAYAAADEUlfTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAIGNIUk0AAHolAACAgwAA+f8AAIDpAAB1MAAA6mAAADqYAAAXb5JfxUYAAABNSURBVHjaXI7BDcAwCAMvyQjMyQ6dAbZiKfqoUK34g2zJh1dENIC7M8pMAPYdzAVY3d0ajNz9aypS/b5R6o+ZPdqoKgCq6h80KH3xDgBqNR97p8oAGQAAAABJRU5ErkJggg== ) no-repeat right -20px center;
border: 1px solid #999;
display: inline-block;
}
#converse-roster .filter-type {
height: 20px;
padding: 0;
margin: 0 0 0 -5px;
}
/* (jQ addClass:) if input has value: */
#converse-roster .roster-filter.x {
background-position: right 3px center;
}
/* (jQ addClass:) if mouse is over the 'x' input area*/
#converse-roster .roster-filter.onX {
cursor: pointer;
}
#converse-roster .roster-contacts {
margin: 0;
overflow-y: auto;
overflow-x: hidden;
max-height: 195px;
max-height: calc(100% - 67px);
}
#converse-roster .group-toggle {
color: #666;
@ -961,7 +989,7 @@ dl.add-converse-contact {
text-shadow: 0 1px 0 #fafafa;
clear: both;
}
#conversejs #converse-roster dd {
#converse-roster dd {
line-height: 16px;
}
#conversejs .group-toggle {
@ -970,20 +998,20 @@ dl.add-converse-contact {
}
#conversejs .roster-group:hover,
#conversejs dd.available-chatroom:hover,
#conversejs #converse-roster dd:hover {
#converse-roster dd:hover {
background-color: #eee;
}
#conversejs #converse-roster dd a.decline-xmpp-request {
#converse-roster dd a.decline-xmpp-request {
margin-left: 5px;
}
#conversejs #converse-roster dd a.remove-xmpp-contact {
#converse-roster dd a.remove-xmpp-contact {
float: right;
width: 22px;
margin: 0;
display: none;
color: #4f4f4f;
}
#conversejs #converse-roster dd:hover a.remove-xmpp-contact {
#converse-roster dd:hover a.remove-xmpp-contact {
display: inline-block;
}
#conversejs .chatbox,

File diff suppressed because one or more lines are too long

View File

@ -1,8 +1,14 @@
Changelog
=========
0.8 (2014-08-04)
----------------
0.8.1 (Unreleased)
------------------
* #212 Provide a live filter of the roster contacts. [jcbrand]
0.8.0 (2014-08-04)
------------------
.. note::
1. Converse.js is now relicensed under the `Mozilla Public License <http://www.mozilla.org/MPL/2.0/>`_.

View File

@ -11,14 +11,8 @@
<link type="text/css" rel="stylesheet" media="screen" href="components/bootstrap/dist/css/bootstrap.min.css" />
<link type="text/css" rel="stylesheet" media="screen" href="components/fontawesome/css/font-awesome.min.css" />
<link type="text/css" rel="stylesheet" media="screen" href="css/theme.css" />
<link type="text/css" rel="stylesheet" media="screen" href="css/converse.min.css" />
<!-- Only for development: <script data-main="main" src="components/requirejs/require.js"></script> -->
<![if gte IE 9]>
<script src="builds/converse.website.min.js"></script>
<![endif]>
<!--[if lt IE 9]>
<script src="builds/converse.website-no-otr.min.js"></script>
<![endif]-->
<link type="text/css" rel="stylesheet" media="screen" href="css/converse.css" />
<script data-main="main" src="components/requirejs/require.js"></script>
</head>
<body id="page-top" data-spy="scroll" data-target=".navbar-custom">

View File

@ -831,7 +831,7 @@ dl.add-converse-contact {
clear: right;
height: 22px;
width: 12px;
padding: 0px 5px 0 0;
padding: 0px 15px 0 0;
color: rgb(79, 79, 79);
}
@ -902,7 +902,7 @@ dl.add-converse-contact {
}
#conversejs .controlbox-pane dt {
padding: 3px;
padding-bottom: 3px;
}
#conversejs .chatroom-form-container {
@ -967,12 +967,12 @@ dl.add-converse-contact {
text-overflow: ellipsis;
}
#conversejs #converse-roster span.req-contact-name {
#converse-roster span.req-contact-name {
width: 65%;
}
#conversejs #converse-roster span.pending-contact-name,
#conversejs #converse-roster a.open-chat {
#converse-roster span.pending-contact-name,
#converse-roster a.open-chat {
width: 80%;
}
@ -981,13 +981,46 @@ dl.add-converse-contact {
}
#converse-roster {
overflow-y: auto;
overflow-x: hidden;
width: 100%;
position: relative;
margin: 0.5em 0 0 0;
height: 254px;
height: ~"calc(100% - 70px)";
height: 194px;
height: ~"calc(100% - 25px)";
overflow-x: hidden;
}
#converse-roster .roster-filter {
padding: 0;
margin: 0 0 5px 0.5em;
width: 111px;
height: 20px;
background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAcAAAAHCAYAAADEUlfTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAIGNIUk0AAHolAACAgwAA+f8AAIDpAAB1MAAA6mAAADqYAAAXb5JfxUYAAABNSURBVHjaXI7BDcAwCAMvyQjMyQ6dAbZiKfqoUK34g2zJh1dENIC7M8pMAPYdzAVY3d0ajNz9aypS/b5R6o+ZPdqoKgCq6h80KH3xDgBqNR97p8oAGQAAAABJRU5ErkJggg== ) no-repeat right -20px center;
border: 1px solid #999;
display: inline-block;
}
#converse-roster .filter-type {
height: 20px;
padding: 0;
margin: 0 0 0 -5px;
}
/* (jQ addClass:) if input has value: */
#converse-roster .roster-filter.x {
background-position: right 3px center;
}
/* (jQ addClass:) if mouse is over the 'x' input area*/
#converse-roster .roster-filter.onX{
cursor:pointer;
}
#converse-roster .roster-contacts {
margin: 0;
overflow-y: auto;
overflow-x: hidden;
max-height: 195px;
max-height: ~"calc(100% - 67px)";
}
#converse-roster .group-toggle {
@ -1073,7 +1106,7 @@ dl.add-converse-contact {
clear: both;
}
#conversejs #converse-roster dd {
#converse-roster dd {
line-height: 16px;
}
@ -1084,15 +1117,15 @@ dl.add-converse-contact {
#conversejs .roster-group:hover,
#conversejs dd.available-chatroom:hover,
#conversejs #converse-roster dd:hover {
#converse-roster dd:hover {
background-color: #eee;
}
#conversejs #converse-roster dd a.decline-xmpp-request {
#converse-roster dd a.decline-xmpp-request {
margin-left: 5px;
}
#conversejs #converse-roster dd a.remove-xmpp-contact {
#converse-roster dd a.remove-xmpp-contact {
float: right;
width: 22px;
margin: 0;
@ -1100,7 +1133,7 @@ dl.add-converse-contact {
color: rgb(79, 79, 79);
}
#conversejs #converse-roster dd:hover a.remove-xmpp-contact {
#converse-roster dd:hover a.remove-xmpp-contact {
display: inline-block;
}

View File

@ -4,6 +4,7 @@ config = {
"jquery": "components/jquery/dist/jquery",
"jquery.browser": "components/jquery.browser/dist/jquery.browser",
"jquery.easing": "components/jquery-easing-original/jquery.easing.1.3", // XXX: Only required for https://conversejs.org website
"utils": "src/utils",
"bootstrap": "components/bootstrap/dist/js/bootstrap", // XXX: Only required for https://conversejs.org website
"locales": "locale/locales",
"underscore": "components/underscore/underscore",
@ -74,6 +75,7 @@ config = {
'bigint': { deps: ['crypto'] },
'jquery.browser': { deps: ['jquery'] },
'jquery.easing': { deps: ['jquery'] },
'utils': { deps: ['jquery'] },
'strophe': { deps: ['jquery'] },
'strophe.disco': { deps: ['strophe'] },
'strophe.muc': { deps: ['strophe', 'jquery'] },

View File

@ -114,139 +114,146 @@
</ul>
</dd>
</dl>
<dl id="converse-roster" style="display: block;">
<dt class="roster-group" style="display: block;">
<a href="#" data-group="Colleagues" class="group-toggle icon-opened" title="Click to hide these contacts">Colleagues</a>
</dt>
<dd class="online current-xmpp-contact">
<a class="open-chat" title="Click to chat with this contact" href="#">
<span class="icon-online" title="This contact is online"></span>
Victor Matfield
</a>
<a class="remove-xmpp-contact icon-remove" title="Click to remove this contact" href="#"></a>
</dd>
<dd class="away current-xmpp-contact">
<a class="open-chat" title="Click to chat with this contact" href="#">
<span class="icon-away" title="this contact is away"></span>
William Winterbottom
</a>
<a class="remove-xmpp-contact icon-remove" title="Click to remove this contact" href="#"></a>
</dd>
<dd class="dnd current-xmpp-contact">
<a class="open-chat" title="Click to chat with this contact" href="#">
<span class="icon-dnd" title="This contact is busy"></span>
Gary Teichmann
</a>
<a class="remove-xmpp-contact icon-remove" title="Click to remove this contact" href="#"></a>
</dd>
<div id="converse-roster">
<input class="roster-filter" placeholder="Type to filter">
<select class="filter-type">
<option value="contacts">Contacts</option>
<option value="groups">Groups</option>
</select>
<dl class="roster-contacts" style="display: block;">
<dt class="roster-group" style="display: block;">
<a href="#" data-group="Colleagues" class="group-toggle icon-opened" title="Click to hide these contacts">Colleagues</a>
</dt>
<dd class="online current-xmpp-contact">
<a class="open-chat" title="Click to chat with this contact" href="#">
<span class="icon-online" title="This contact is online"></span>
Victor Matfield
</a>
<a class="remove-xmpp-contact icon-remove" title="Click to remove this contact" href="#"></a>
</dd>
<dd class="away current-xmpp-contact">
<a class="open-chat" title="Click to chat with this contact" href="#">
<span class="icon-away" title="this contact is away"></span>
William Winterbottom
</a>
<a class="remove-xmpp-contact icon-remove" title="Click to remove this contact" href="#"></a>
</dd>
<dd class="dnd current-xmpp-contact">
<a class="open-chat" title="Click to chat with this contact" href="#">
<span class="icon-dnd" title="This contact is busy"></span>
Gary Teichmann
</a>
<a class="remove-xmpp-contact icon-remove" title="Click to remove this contact" href="#"></a>
</dd>
<dt class="roster-group" style="display: block;">
<a href="#" data-group="Family" class="group-toggle icon-opened" title="Click to hide these contacts">Family</a>
</dt>
<dd class="away current-xmpp-contact">
<a class="open-chat" title="Click to chat with this contact" href="#">
<span class="icon-away" title="this contact is away"></span>
Allan Donald
</a>
<a class="remove-xmpp-contact icon-remove" title="Click to remove this contact" href="#"></a>
</dd>
<dd class="offline current-xmpp-contact">
<a class="open-chat" title="Click to chat with this contact" href="#">
<span class="icon-offline" title="This contact is offline"></span>
Corné Krige
</a>
<a class="remove-xmpp-contact icon-remove" title="Click to remove this contact" href="#"></a>
</dd>
<dt class="roster-group" style="display: block;">
<a href="#" data-group="Family" class="group-toggle icon-opened" title="Click to hide these contacts">Family</a>
</dt>
<dd class="away current-xmpp-contact">
<a class="open-chat" title="Click to chat with this contact" href="#">
<span class="icon-away" title="this contact is away"></span>
Allan Donald
</a>
<a class="remove-xmpp-contact icon-remove" title="Click to remove this contact" href="#"></a>
</dd>
<dd class="offline current-xmpp-contact">
<a class="open-chat" title="Click to chat with this contact" href="#">
<span class="icon-offline" title="This contact is offline"></span>
Corné Krige
</a>
<a class="remove-xmpp-contact icon-remove" title="Click to remove this contact" href="#"></a>
</dd>
<dt class="roster-group" style="display: block;">
<a href="#" data-group="Friends" class="group-toggle icon-opened" title="Click to hide these contacts">Friends</a>
</dt>
<dd class="online current-xmpp-contact">
<a class="open-chat" title="Click to chat with this contact" href="#">
<span class="icon-online" title="This contact is online"></span>
John Smit
</a>
<a class="remove-xmpp-contact icon-remove" title="Click to remove this contact" href="#"></a>
</dd>
<dd class="online current-xmpp-contact">
<a class="open-chat" title="Click to chat with this contact" href="#">
<span class="icon-online" title="This contact is online"></span>
Bakkies Botha
</a>
<a class="remove-xmpp-contact icon-remove" title="Click to remove this contact" href="#"></a>
</dd>
<dt class="roster-group" style="display: block;">
<a href="#" data-group="Friends" class="group-toggle icon-opened" title="Click to hide these contacts">Friends</a>
</dt>
<dd class="online current-xmpp-contact">
<a class="open-chat" title="Click to chat with this contact" href="#">
<span class="icon-online" title="This contact is online"></span>
John Smit
</a>
<a class="remove-xmpp-contact icon-remove" title="Click to remove this contact" href="#"></a>
</dd>
<dd class="online current-xmpp-contact">
<a class="open-chat" title="Click to chat with this contact" href="#">
<span class="icon-online" title="This contact is online"></span>
Bakkies Botha
</a>
<a class="remove-xmpp-contact icon-remove" title="Click to remove this contact" href="#"></a>
</dd>
<dt class="roster-group" style="display: block;">
<a href="#" class="group-toggle icon-opened" title="Click to hide these contacts">Ungrouped</a>
</dt>
<dd class="online current-xmpp-contact">
<a class="open-chat" title="Click to chat with this contact" href="#">
<span class="icon-online" title="This contact is online"></span>
James Small
</a>
<a class="remove-xmpp-contact icon-remove" title="Click to remove this contact" href="#"></a>
</dd>
<dt class="roster-group" style="display: block;">
<a href="#" class="group-toggle icon-opened" title="Click to hide these contacts">Ungrouped</a>
</dt>
<dd class="online current-xmpp-contact">
<a class="open-chat" title="Click to chat with this contact" href="#">
<span class="icon-online" title="This contact is online"></span>
James Small
</a>
<a class="remove-xmpp-contact icon-remove" title="Click to remove this contact" href="#"></a>
</dd>
<dt id="xmpp-contact-requests" style="display: block;">
<a href="#" class="group-toggle icon-opened" title="Click to hide these contacts">Contact Requests</a>
</dt>
<dd class="offline requesting-xmpp-contact">
<span class="req-contact-name">Bob Skinstad</span>
<span class="request-actions">
<a class="accept-xmpp-request icon-checkmark" title="Click here to accept this contact's request" href="#"></a>
<a class="decline-xmpp-request icon-close" title="Click here to decline this contact's request" href="#"></a>
</span>
</dd>
<dd class="offline requesting-xmpp-contact">
<span class="req-contact-name">André Vos</span>
<span class="request-actions">
<a class="accept-xmpp-request icon-checkmark" title="Click here to accept this contact's request" href="#"></a>
<a class="decline-xmpp-request icon-close" title="Click here to decline this contact's request" href="#"></a>
</span>
</dd>
<dt id="xmpp-contact-requests" style="display: block;">
<a href="#" class="group-toggle icon-opened" title="Click to hide these contacts">Contact Requests</a>
</dt>
<dd class="offline requesting-xmpp-contact">
<span class="req-contact-name">Bob Skinstad</span>
<span class="request-actions">
<a class="accept-xmpp-request icon-checkmark" title="Click here to accept this contact's request" href="#"></a>
<a class="decline-xmpp-request icon-close" title="Click here to decline this contact's request" href="#"></a>
</span>
</dd>
<dd class="offline requesting-xmpp-contact">
<span class="req-contact-name">André Vos</span>
<span class="request-actions">
<a class="accept-xmpp-request icon-checkmark" title="Click here to accept this contact's request" href="#"></a>
<a class="decline-xmpp-request icon-close" title="Click here to decline this contact's request" href="#"></a>
</span>
</dd>
<dt id="pending-xmpp-contacts" style="display: block;">
<a href="#" class="group-toggle icon-opened" title="Click to hide these contacts">Pending Contacts</a>
</dt>
<dd class="offline pending-xmpp-contact"><span class="pending-contact-name">Rassie Erasmus</span>
<a class="remove-xmpp-contact icon-remove" title="Click to remove this contact" href="#"></a>
</dd>
<dd class="offline pending-xmpp-contact"><span class="pending-contact-name">Victor Matfield</span>
<a class="remove-xmpp-contact icon-remove" title="Click to remove this contact" href="#"></a>
</dd>
</dl>
</div>
<div id="chatrooms" style="display: none;">
<form class="add-chatroom" action="" method="post">
<input type="text" name="chatroom" class="new-chatroom-name" placeholder="Room name">
<input type="text" name="nick" class="new-chatroom-nick" placeholder="Nickname">
<input type="text" name="server" class="new-chatroom-server" placeholder="Server">
<input type="submit" name="join" value="Join">
<input type="button" name="show" id="show-rooms" value="Show rooms" style="display: inline-block;">
</form>
<dl id="available-chatrooms">
<dt>Rooms on conference.opkode.im</dt>
<dd class="available-chatroom">
<a class="open-room"
data-room-jid="converse.js@conference.opkode.im"
title="Click to open this room" href="#">Special chatroom with a long name (2)</a>
<a class="room-info icon-room-info"
data-room-jid="converse.js@conference.opkode.im"
title="Show more information on this room" href="#">&nbsp;</a>
<div class="room-info">
<p class="room-info"><strong>Description:</strong></p>
<p class="room-info"><strong>Occupants:</strong> 2</p>
<p class="room-info"><strong>Features:</strong> </p>
<ul>
<li class="room-info">Moderated</li><li class="room-info">Open room</li>
<li class="room-info">Permanent room</li><li class="room-info">Public</li>
<li class="room-info">Semi-anonymous</li>
<li class="room-info">Requires authentication <span class="icon-lock"></span></li>
<p></p>
</ul>
</div>
</dd>
</dl>
<dt id="pending-xmpp-contacts" style="display: block;">
<a href="#" class="group-toggle icon-opened" title="Click to hide these contacts">Pending Contacts</a>
</dt>
<dd class="offline pending-xmpp-contact"><span class="pending-contact-name">Rassie Erasmus</span>
<a class="remove-xmpp-contact icon-remove" title="Click to remove this contact" href="#"></a>
</dd>
<dd class="offline pending-xmpp-contact"><span class="pending-contact-name">Victor Matfield</span>
<a class="remove-xmpp-contact icon-remove" title="Click to remove this contact" href="#"></a>
</dd>
</dl>
</div>
<div id="chatrooms" style="display: none;">
<form class="add-chatroom" action="" method="post">
<input type="text" name="chatroom" class="new-chatroom-name" placeholder="Room name">
<input type="text" name="nick" class="new-chatroom-nick" placeholder="Nickname">
<input type="text" name="server" class="new-chatroom-server" placeholder="Server">
<input type="submit" name="join" value="Join">
<input type="button" name="show" id="show-rooms" value="Show rooms" style="display: inline-block;">
</form>
<dl id="available-chatrooms">
<dt>Rooms on conference.opkode.im</dt>
<dd class="available-chatroom">
<a class="open-room"
data-room-jid="converse.js@conference.opkode.im"
title="Click to open this room" href="#">Special chatroom with a long name (2)</a>
<a class="room-info icon-room-info"
data-room-jid="converse.js@conference.opkode.im"
title="Show more information on this room" href="#">&nbsp;</a>
<div class="room-info">
<p class="room-info"><strong>Description:</strong></p>
<p class="room-info"><strong>Occupants:</strong> 2</p>
<p class="room-info"><strong>Features:</strong> </p>
<ul>
<li class="room-info">Moderated</li><li class="room-info">Open room</li>
<li class="room-info">Permanent room</li><li class="room-info">Public</li>
<li class="room-info">Semi-anonymous</li>
<li class="room-info">Requires authentication <span class="icon-lock"></span></li>
<p></p>
</ul>
</div>
</dd>
</dl>
</div>
</div>
</div>
</div>

View File

@ -114,89 +114,146 @@
</ul>
</dd>
</dl>
<dl id="converse-roster" style="display: block;">
<dt id="xmpp-contacts" style="display: block;">My contacts</dt>
<dd class="online current-xmpp-contact">
<a class="open-chat" title="Click to chat with this contact" href="#">
<span class="icon-online" title="This contact is online"></span>
John Smit</a>
<a class="remove-xmpp-contact icon-remove" title="Click to remove this contact" href="#"></a>
</dd>
<dd class="away current-xmpp-contact">
<a class="open-chat" title="Click to chat with this contact" href="#">
<span class="icon-away" title="this contact is away"></span>
Francois Pienaar</a>
<a class="remove-xmpp-contact icon-remove" title="Click to remove this contact" href="#"></a>
</dd>
<dd class="dnd current-xmpp-contact">
<a class="open-chat" title="Click to chat with this contact" href="#">
<span class="icon-dnd" title="This contact is busy"></span>
Gary Teichmann</a>
<a class="remove-xmpp-contact icon-remove" title="Click to remove this contact" href="#"></a>
</dd>
<dd class="offline current-xmpp-contact">
<a class="open-chat" title="Click to chat with this contact" href="#">
<span class="icon-offline" title="This contact is offline"></span>
Corné Krige</a>
<a class="remove-xmpp-contact icon-remove" title="Click to remove this contact" href="#"></a>
</dd>
<div id="converse-roster">
<input class="roster-filter" placeholder="Type to filter">
<select class="filter-type">
<option value="contacts">Contacts</option>
<option value="groups">Groups</option>
</select>
<dl class="roster-contacts" style="display: block;">
<dt class="roster-group" style="display: block;">
<a href="#" data-group="Colleagues" class="group-toggle icon-opened" title="Click to hide these contacts">Colleagues</a>
</dt>
<dd class="online current-xmpp-contact">
<a class="open-chat" title="Click to chat with this contact" href="#">
<span class="icon-online" title="This contact is online"></span>
Victor Matfield
</a>
<a class="remove-xmpp-contact icon-remove" title="Click to remove this contact" href="#"></a>
</dd>
<dd class="away current-xmpp-contact">
<a class="open-chat" title="Click to chat with this contact" href="#">
<span class="icon-away" title="this contact is away"></span>
William Winterbottom
</a>
<a class="remove-xmpp-contact icon-remove" title="Click to remove this contact" href="#"></a>
</dd>
<dd class="dnd current-xmpp-contact">
<a class="open-chat" title="Click to chat with this contact" href="#">
<span class="icon-dnd" title="This contact is busy"></span>
Gary Teichmann
</a>
<a class="remove-xmpp-contact icon-remove" title="Click to remove this contact" href="#"></a>
</dd>
<dt id="xmpp-contact-requests" style="display: block;">Contact requests</dt>
<dd class="offline requesting-xmpp-contact">
<span>Bob Skinstad</span>
<span class="request-actions">
<a class="accept-xmpp-request icon-checkmark" title="Click here to accept this contact's request" href="#"></a>
<a class="decline-xmpp-request icon-close" title="Click here to decline this contact's request" href="#"></a>
</span>
</dd>
<dd class="offline requesting-xmpp-contact">
<span>André Vos</span>
<span class="request-actions">
<a class="accept-xmpp-request icon-checkmark" title="Click here to accept this contact's request" href="#"></a>
<a class="decline-xmpp-request icon-close" title="Click here to decline this contact's request" href="#"></a>
</span>
</dd>
<dt class="roster-group" style="display: block;">
<a href="#" data-group="Family" class="group-toggle icon-opened" title="Click to hide these contacts">Family</a>
</dt>
<dd class="away current-xmpp-contact">
<a class="open-chat" title="Click to chat with this contact" href="#">
<span class="icon-away" title="this contact is away"></span>
Allan Donald
</a>
<a class="remove-xmpp-contact icon-remove" title="Click to remove this contact" href="#"></a>
</dd>
<dd class="offline current-xmpp-contact">
<a class="open-chat" title="Click to chat with this contact" href="#">
<span class="icon-offline" title="This contact is offline"></span>
Corné Krige
</a>
<a class="remove-xmpp-contact icon-remove" title="Click to remove this contact" href="#"></a>
</dd>
<dt id="pending-xmpp-contacts" style="display: block;">Pending contacts</dt>
<dd class="offline pending-xmpp-contact"><span>Rassie Erasmus</span>
<a class="remove-xmpp-contact icon-remove" title="Click to remove this contact" href="#"></a>
</dd>
<dd class="offline pending-xmpp-contact"><span>Victor Matfield</span>
<a class="remove-xmpp-contact icon-remove" title="Click to remove this contact" href="#"></a>
</dd>
</dl>
</div>
<div id="chatrooms" style="display: none;">
<form class="add-chatroom" action="" method="post">
<input type="text" name="chatroom" class="new-chatroom-name" placeholder="Room name">
<input type="text" name="nick" class="new-chatroom-nick" placeholder="Nickname">
<input type="text" name="server" class="new-chatroom-server" placeholder="Server">
<input type="submit" name="join" value="Join">
<input type="button" name="show" id="show-rooms" value="Show rooms" style="display: inline-block;">
</form>
<dl id="available-chatrooms">
<dt>Rooms on conference.opkode.im</dt>
<dd class="available-chatroom">
<a class="open-room"
data-room-jid="converse.js@conference.opkode.im"
title="Click to open this room" href="#">Special chatroom with a long name (2)</a>
<a class="room-info icon-room-info"
data-room-jid="converse.js@conference.opkode.im"
title="Show more information on this room" href="#">&nbsp;</a>
<div class="room-info">
<p class="room-info"><strong>Description:</strong></p>
<p class="room-info"><strong>Occupants:</strong> 2</p>
<p class="room-info"><strong>Features:</strong> </p>
<ul>
<li class="room-info">Moderated</li><li class="room-info">Open room</li>
<li class="room-info">Permanent room</li><li class="room-info">Public</li>
<li class="room-info">Semi-anonymous</li>
<li class="room-info">Requires authentication <span class="icon-lock"></span></li>
<p></p>
</ul>
</div>
</dd>
</dl>
<dt class="roster-group" style="display: block;">
<a href="#" data-group="Friends" class="group-toggle icon-opened" title="Click to hide these contacts">Friends</a>
</dt>
<dd class="online current-xmpp-contact">
<a class="open-chat" title="Click to chat with this contact" href="#">
<span class="icon-online" title="This contact is online"></span>
John Smit
</a>
<a class="remove-xmpp-contact icon-remove" title="Click to remove this contact" href="#"></a>
</dd>
<dd class="online current-xmpp-contact">
<a class="open-chat" title="Click to chat with this contact" href="#">
<span class="icon-online" title="This contact is online"></span>
Bakkies Botha
</a>
<a class="remove-xmpp-contact icon-remove" title="Click to remove this contact" href="#"></a>
</dd>
<dt class="roster-group" style="display: block;">
<a href="#" class="group-toggle icon-opened" title="Click to hide these contacts">Ungrouped</a>
</dt>
<dd class="online current-xmpp-contact">
<a class="open-chat" title="Click to chat with this contact" href="#">
<span class="icon-online" title="This contact is online"></span>
James Small
</a>
<a class="remove-xmpp-contact icon-remove" title="Click to remove this contact" href="#"></a>
</dd>
<dt id="xmpp-contact-requests" style="display: block;">
<a href="#" class="group-toggle icon-opened" title="Click to hide these contacts">Contact Requests</a>
</dt>
<dd class="offline requesting-xmpp-contact">
<span class="req-contact-name">Bob Skinstad</span>
<span class="request-actions">
<a class="accept-xmpp-request icon-checkmark" title="Click here to accept this contact's request" href="#"></a>
<a class="decline-xmpp-request icon-close" title="Click here to decline this contact's request" href="#"></a>
</span>
</dd>
<dd class="offline requesting-xmpp-contact">
<span class="req-contact-name">André Vos</span>
<span class="request-actions">
<a class="accept-xmpp-request icon-checkmark" title="Click here to accept this contact's request" href="#"></a>
<a class="decline-xmpp-request icon-close" title="Click here to decline this contact's request" href="#"></a>
</span>
</dd>
<dt id="pending-xmpp-contacts" style="display: block;">
<a href="#" class="group-toggle icon-opened" title="Click to hide these contacts">Pending Contacts</a>
</dt>
<dd class="offline pending-xmpp-contact"><span class="pending-contact-name">Rassie Erasmus</span>
<a class="remove-xmpp-contact icon-remove" title="Click to remove this contact" href="#"></a>
</dd>
<dd class="offline pending-xmpp-contact"><span class="pending-contact-name">Victor Matfield</span>
<a class="remove-xmpp-contact icon-remove" title="Click to remove this contact" href="#"></a>
</dd>
</dl>
</div>
<div id="chatrooms" style="display: none;">
<form class="add-chatroom" action="" method="post">
<input type="text" name="chatroom" class="new-chatroom-name" placeholder="Room name">
<input type="text" name="nick" class="new-chatroom-nick" placeholder="Nickname">
<input type="text" name="server" class="new-chatroom-server" placeholder="Server">
<input type="submit" name="join" value="Join">
<input type="button" name="show" id="show-rooms" value="Show rooms" style="display: inline-block;">
</form>
<dl id="available-chatrooms">
<dt>Rooms on conference.opkode.im</dt>
<dd class="available-chatroom">
<a class="open-room"
data-room-jid="converse.js@conference.opkode.im"
title="Click to open this room" href="#">Special chatroom with a long name (2)</a>
<a class="room-info icon-room-info"
data-room-jid="converse.js@conference.opkode.im"
title="Show more information on this room" href="#">&nbsp;</a>
<div class="room-info">
<p class="room-info"><strong>Description:</strong></p>
<p class="room-info"><strong>Occupants:</strong> 2</p>
<p class="room-info"><strong>Features:</strong> </p>
<ul>
<li class="room-info">Moderated</li><li class="room-info">Open room</li>
<li class="room-info">Permanent room</li><li class="room-info">Public</li>
<li class="room-info">Semi-anonymous</li>
<li class="room-info">Requires authentication <span class="icon-lock"></span></li>
<p></p>
</ul>
</div>
</dd>
</dl>
</div>
</div>
</div>
</div>

View File

@ -1,23 +1,23 @@
(function (root, factory) {
define([
"mock",
"utils"
], function (mock, utils) {
return factory(mock, utils);
"test_utils"
], function (mock, test_utils) {
return factory(mock, test_utils);
}
);
} (this, function (mock, utils) {
return describe("Chatboxes", $.proxy(function(mock, utils) {
} (this, function (mock, test_utils) {
return describe("Chatboxes", $.proxy(function(mock, test_utils) {
describe("A Chatbox", $.proxy(function () {
beforeEach(function () {
runs(function () {
utils.closeAllChatBoxes();
utils.removeControlBox();
utils.clearBrowserStorage();
utils.initConverse();
utils.createContacts();
utils.openControlBox();
utils.openContactsPanel();
test_utils.closeAllChatBoxes();
test_utils.removeControlBox();
test_utils.clearBrowserStorage();
test_utils.initConverse();
test_utils.createContacts();
test_utils.openControlBox();
test_utils.openContactsPanel();
});
});
@ -95,7 +95,7 @@
// openControlBox was called earlier, so the controlbox is
// visible, but no other chat boxes have been created.
expect(this.chatboxes.length).toEqual(1);
chatbox = utils.openChatBoxFor(contact_jid);
chatbox = test_utils.openChatBoxFor(contact_jid);
chatboxview = this.chatboxviews.get(contact_jid);
spyOn(chatboxview, 'focus');
$el = this.rosterview.$el.find('a.open-chat:contains("'+chatbox.get('fullname')+'")');
@ -109,11 +109,11 @@
spyOn(converse, 'emit');
spyOn(this.chatboxviews, 'trimChats');
runs(function () {
utils.openControlBox();
test_utils.openControlBox();
});
waits(250);
runs(function () {
utils.openChatBoxes(6);
test_utils.openChatBoxes(6);
expect(this.chatboxviews.trimChats).toHaveBeenCalled();
// We instantiate a new ChatBoxes collection, which by default
// will be empty.
@ -136,7 +136,7 @@
}, converse));
it("can be closed by clicking a DOM element with class 'close-chatbox-button'", $.proxy(function () {
var chatbox = utils.openChatBoxes(1)[0],
var chatbox = test_utils.openChatBoxes(1)[0],
controlview = this.chatboxviews.get('controlbox'), // The controlbox is currently open
chatview = this.chatboxviews.get(chatbox.get('jid'));
spyOn(chatview, 'close').andCallThrough();
@ -166,7 +166,7 @@
}, converse));
it("can be minimized by clicking a DOM element with class 'toggle-chatbox-button'", function () {
var chatbox = utils.openChatBoxes(1)[0],
var chatbox = test_utils.openChatBoxes(1)[0],
chatview = this.chatboxviews.get(chatbox.get('jid')),
trimmed_chatboxes = this.minimized_chats,
trimmedview;
@ -207,17 +207,17 @@
spyOn(converse.chatboxviews, 'trimChats');
this.chatboxes.browserStorage._clear();
runs(function () {
utils.closeControlBox();
test_utils.closeControlBox();
});
waits(250);
runs(function () {
expect(converse.emit).toHaveBeenCalledWith('chatBoxClosed', jasmine.any(Object));
expect(converse.chatboxes.length).toEqual(0);
utils.openChatBoxes(6);
test_utils.openChatBoxes(6);
expect(converse.chatboxviews.trimChats).toHaveBeenCalled();
expect(converse.chatboxes.length).toEqual(6);
expect(converse.emit).toHaveBeenCalledWith('chatBoxOpened', jasmine.any(Object));
utils.closeAllChatBoxes();
test_utils.closeAllChatBoxes();
});
waits(250);
runs(function () {
@ -237,7 +237,7 @@
describe("A chat toolbar", $.proxy(function () {
it("can be found on each chat box", $.proxy(function () {
var contact_jid = mock.cur_names[2].replace(/ /g,'.').toLowerCase() + '@localhost';
utils.openChatBoxFor(contact_jid);
test_utils.openChatBoxFor(contact_jid);
var chatbox = this.chatboxes.get(contact_jid);
var view = this.chatboxviews.get(contact_jid);
expect(chatbox).toBeDefined();
@ -249,7 +249,7 @@
it("contains a button for inserting emoticons", $.proxy(function () {
var contact_jid = mock.cur_names[2].replace(/ /g,'.').toLowerCase() + '@localhost';
utils.openChatBoxFor(contact_jid);
test_utils.openChatBoxFor(contact_jid);
var view = this.chatboxviews.get(contact_jid);
var $toolbar = view.$el.find('ul.chat-toolbar');
var $textarea = view.$el.find('textarea.chat-textarea');
@ -308,7 +308,7 @@
it("contains a button for starting an encrypted chat session", $.proxy(function () {
// TODO: More tests can be added here...
var contact_jid = mock.cur_names[2].replace(/ /g,'.').toLowerCase() + '@localhost';
utils.openChatBoxFor(contact_jid);
test_utils.openChatBoxFor(contact_jid);
var view = this.chatboxviews.get(contact_jid);
var $toolbar = view.$el.find('ul.chat-toolbar');
expect($toolbar.children('li.toggle-otr').length).toBe(1);
@ -336,7 +336,7 @@
// First check that the button doesn't show if it's not enabled
// via "visible_toolbar_buttons"
converse.visible_toolbar_buttons.call = false;
utils.openChatBoxFor(contact_jid);
test_utils.openChatBoxFor(contact_jid);
view = this.chatboxviews.get(contact_jid);
$toolbar = view.$el.find('ul.chat-toolbar');
callButton = $toolbar.find('.toggle-call');
@ -345,7 +345,7 @@
// Now check that it's shown if enabled and that it emits
// callButtonClicked
converse.visible_toolbar_buttons.call = true; // enable the button
utils.openChatBoxFor(contact_jid);
test_utils.openChatBoxFor(contact_jid);
view = this.chatboxviews.get(contact_jid);
$toolbar = view.$el.find('ul.chat-toolbar');
callButton = $toolbar.find('.toggle-call');
@ -360,7 +360,7 @@
// First check that the button doesn't show if it's not enabled
// via "visible_toolbar_buttons"
converse.visible_toolbar_buttons.clear = false;
utils.openChatBoxFor(contact_jid);
test_utils.openChatBoxFor(contact_jid);
view = this.chatboxviews.get(contact_jid);
view = this.chatboxviews.get(contact_jid);
$toolbar = view.$el.find('ul.chat-toolbar');
@ -370,7 +370,7 @@
// Now check that it's shown if enabled and that it calls
// clearMessages
converse.visible_toolbar_buttons.clear = true; // enable the button
utils.openChatBoxFor(contact_jid);
test_utils.openChatBoxFor(contact_jid);
view = this.chatboxviews.get(contact_jid);
$toolbar = view.$el.find('ul.chat-toolbar');
clearButton = $toolbar.find('.toggle-clear');
@ -387,7 +387,7 @@
beforeEach(function () {
runs(function () {
utils.closeAllChatBoxes();
test_utils.closeAllChatBoxes();
});
waits(250);
runs(function () {});
@ -445,7 +445,7 @@
var contact_jid = contact_name.replace(/ /g,'.').toLowerCase() + '@localhost';
spyOn(this, 'emit');
runs(function () {
utils.openChatBoxFor(contact_jid);
test_utils.openChatBoxFor(contact_jid);
var chatview = converse.chatboxviews.get(contact_jid);
expect(chatview.model.get('minimized')).toBeFalsy();
chatview.$el.find('.toggle-chatbox-button').click();
@ -505,8 +505,8 @@
spyOn(converse, 'emit');
var contact_name = mock.cur_names[1];
var contact_jid = contact_name.replace(/ /g,'.').toLowerCase() + '@localhost';
utils.openChatBoxFor(contact_jid);
utils.clearChatBoxMessages(contact_jid);
test_utils.openChatBoxFor(contact_jid);
test_utils.clearChatBoxMessages(contact_jid);
var one_day_ago = moment();
one_day_ago.subtract('days', 1);
var message = 'This is a day old message';
@ -574,7 +574,7 @@
spyOn(converse, 'emit');
var contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
runs(function () {
utils.openChatBoxFor(contact_jid);
test_utils.openChatBoxFor(contact_jid);
});
waits(250);
runs(function () {
@ -582,7 +582,7 @@
var view = this.chatboxviews.get(contact_jid);
var message = 'This message is sent from this chatbox';
spyOn(view, 'sendMessage').andCallThrough();
utils.sendMessage(view, message);
test_utils.sendMessage(view, message);
expect(view.sendMessage).toHaveBeenCalled();
expect(view.model.messages.length, 2);
expect(converse.emit.mostRecentCall.args, ['messageSend', message]);
@ -592,11 +592,11 @@
it("is sanitized to prevent Javascript injection attacks", $.proxy(function () {
var contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
utils.openChatBoxFor(contact_jid);
test_utils.openChatBoxFor(contact_jid);
var view = this.chatboxviews.get(contact_jid);
var message = '<p>This message contains <em>some</em> <b>markup</b></p>';
spyOn(view, 'sendMessage').andCallThrough();
utils.sendMessage(view, message);
test_utils.sendMessage(view, message);
expect(view.sendMessage).toHaveBeenCalled();
var msg = view.$el.find('.chat-content').find('.chat-message').last().find('.chat-message-content');
expect(msg.text()).toEqual(message);
@ -605,11 +605,11 @@
it("can contain hyperlinks, which will be clickable", $.proxy(function () {
var contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
utils.openChatBoxFor(contact_jid);
test_utils.openChatBoxFor(contact_jid);
var view = this.chatboxviews.get(contact_jid);
var message = 'This message contains a hyperlink: www.opkode.com';
spyOn(view, 'sendMessage').andCallThrough();
utils.sendMessage(view, message);
test_utils.sendMessage(view, message);
expect(view.sendMessage).toHaveBeenCalled();
var msg = view.$el.find('.chat-content').find('.chat-message').last().find('.chat-message-content');
expect(msg.text()).toEqual(message);
@ -618,7 +618,7 @@
it("should display emoticons correctly", $.proxy(function () {
var contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
utils.openChatBoxFor(contact_jid);
test_utils.openChatBoxFor(contact_jid);
var view = this.chatboxviews.get(contact_jid);
var messages = [':)', ';)', ':D', ':P', '8)', '>:)', ':S', ':\\', '>:(', ':(', ':O', '(^.^)b', '<3'];
var emoticons = [
@ -633,7 +633,7 @@
spyOn(view, 'sendMessage').andCallThrough();
for (var i = 0; i < messages.length; i++) {
var message = messages[i];
utils.sendMessage(view, message);
test_utils.sendMessage(view, message);
expect(view.sendMessage).toHaveBeenCalled();
var msg = view.$el.find('.chat-content').find('.chat-message').last().find('.chat-message-content');
expect(msg.html()).toEqual(emoticons[i]);
@ -642,33 +642,33 @@
it("will have properly escaped URLs", $.proxy(function () {
var contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
utils.openChatBoxFor(contact_jid);
test_utils.openChatBoxFor(contact_jid);
var view = this.chatboxviews.get(contact_jid);
spyOn(view, 'sendMessage').andCallThrough();
var message = "http://www.opkode.com/'onmouseover='alert(1)'whatever";
utils.sendMessage(view, message);
test_utils.sendMessage(view, message);
expect(view.sendMessage).toHaveBeenCalled();
var msg = view.$el.find('.chat-content').find('.chat-message').last().find('.chat-message-content');
expect(msg.text()).toEqual(message);
expect(msg.html()).toEqual('<a target="_blank" href="http://www.opkode.com/%27onmouseover=%27alert%281%29%27whatever">http://www.opkode.com/\'onmouseover=\'alert(1)\'whatever</a>');
message = 'http://www.opkode.com/"onmouseover="alert(1)"whatever';
utils.sendMessage(view, message);
test_utils.sendMessage(view, message);
expect(view.sendMessage).toHaveBeenCalled();
msg = view.$el.find('.chat-content').find('.chat-message').last().find('.chat-message-content');
expect(msg.text()).toEqual(message);
expect(msg.html()).toEqual('<a target="_blank" href="http://www.opkode.com/%22onmouseover=%22alert%281%29%22whatever">http://www.opkode.com/"onmouseover="alert(1)"whatever</a>');
message = "https://en.wikipedia.org/wiki/Ender's_Game";
utils.sendMessage(view, message);
test_utils.sendMessage(view, message);
expect(view.sendMessage).toHaveBeenCalled();
msg = view.$el.find('.chat-content').find('.chat-message').last().find('.chat-message-content');
expect(msg.text()).toEqual(message);
expect(msg.html()).toEqual('<a target="_blank" href="https://en.wikipedia.org/wiki/Ender%27s_Game">https://en.wikipedia.org/wiki/Ender\'s_Game</a>');
message = "https://en.wikipedia.org/wiki/Ender%27s_Game";
utils.sendMessage(view, message);
test_utils.sendMessage(view, message);
expect(view.sendMessage).toHaveBeenCalled();
msg = view.$el.find('.chat-content').find('.chat-message').last().find('.chat-message-content');
expect(msg.text()).toEqual(message);
@ -680,24 +680,24 @@
describe("Special Messages", $.proxy(function () {
beforeEach(function () {
utils.closeAllChatBoxes();
utils.removeControlBox();
test_utils.closeAllChatBoxes();
test_utils.removeControlBox();
converse.roster.browserStorage._clear();
utils.initConverse();
utils.createContacts();
utils.openControlBox();
utils.openContactsPanel();
test_utils.initConverse();
test_utils.createContacts();
test_utils.openControlBox();
test_utils.openContactsPanel();
});
it("'/clear' can be used to clear messages in a conversation", $.proxy(function () {
spyOn(converse, 'emit');
var contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
utils.openChatBoxFor(contact_jid);
test_utils.openChatBoxFor(contact_jid);
var view = this.chatboxviews.get(contact_jid);
var message = 'This message is another sent from this chatbox';
// Lets make sure there is at least one message already
// (e.g for when this test is run on its own).
utils.sendMessage(view, message);
test_utils.sendMessage(view, message);
expect(view.model.messages.length > 0).toBeTruthy();
expect(view.model.messages.browserStorage.records.length > 0).toBeTruthy();
expect(converse.emit).toHaveBeenCalledWith('messageSend', message);
@ -709,7 +709,7 @@
spyOn(window, 'confirm').andCallFake(function () {
return true;
});
utils.sendMessage(view, message);
test_utils.sendMessage(view, message);
expect(view.sendMessage).toHaveBeenCalled();
expect(view.clearMessages).toHaveBeenCalled();
expect(window.confirm).toHaveBeenCalled();
@ -775,5 +775,5 @@
expect(this.msg_counter).toBe(0);
}, converse));
}, converse));
}, converse, mock, utils));
}, converse, mock, test_utils));
}));

View File

@ -1,25 +1,25 @@
(function (root, factory) {
define([
"mock",
"utils"
], function (mock, utils) {
return factory(mock, utils);
"test_utils"
], function (mock, test_utils) {
return factory(mock, test_utils);
}
);
} (this, function (mock, utils) {
return describe("ChatRooms", $.proxy(function (mock, utils) {
} (this, function (mock, test_utils) {
return describe("ChatRooms", $.proxy(function (mock, test_utils) {
describe("A Chat Room", $.proxy(function () {
beforeEach(function () {
runs(function () {
utils.closeAllChatBoxes();
test_utils.closeAllChatBoxes();
});
waits(250);
runs(function () {
utils.openControlBox();
test_utils.openControlBox();
});
waits(250);
runs(function () {
utils.openRoomsPanel();
test_utils.openRoomsPanel();
});
waits(501);
runs(function () {
@ -35,7 +35,7 @@
});
waits(250);
runs(function () {
utils.closeControlBox();
test_utils.closeControlBox();
});
waits(250);
runs(function () {});
@ -116,7 +116,7 @@
// We instantiate a new ChatBoxes collection, which by default
// will be empty.
spyOn(this.chatboxviews, 'trimChats');
utils.openControlBox();
test_utils.openControlBox();
var newchatboxes = new this.ChatBoxes();
expect(newchatboxes.length).toEqual(0);
// The chatboxes will then be fetched from browserStorage inside the
@ -350,5 +350,5 @@
expect(view.$el.find('.chat-body p').text()).toBe("This room has reached it's maximum number of occupants");
}, converse));
}, converse));
}, converse, mock, utils));
}, converse, mock, test_utils));
}));

View File

@ -1,12 +1,12 @@
(function (root, factory) {
define([
"mock",
"utils"
], function (mock, utils) {
return factory(mock, utils);
"test_utils"
], function (mock, test_utils) {
return factory(mock, test_utils);
}
);
} (this, function (mock, utils) {
} (this, function (mock, test_utils) {
var checkHeaderToggling = function ($header) {
var $toggle = $header.find('a.group-toggle');
@ -24,16 +24,16 @@
expect($header.nextUntil('dt', 'dd').length === $header.nextUntil('dt', 'dd:visible').length).toBeTruthy();
};
describe("The Control Box", $.proxy(function (mock, utils) {
describe("The Control Box", $.proxy(function (mock, test_utils) {
beforeEach(function () {
runs(function () {
utils.openControlBox();
test_utils.openControlBox();
});
});
it("can be opened by clicking a DOM element with class 'toggle-controlbox'", $.proxy(function () {
runs(function () {
utils.closeControlBox();
test_utils.closeControlBox();
});
waits(50);
runs(function () {
@ -59,7 +59,7 @@
describe("The Status Widget", $.proxy(function () {
beforeEach(function () {
utils.openControlBox();
test_utils.openControlBox();
});
it("shows the user's chat status, which is online by default", $.proxy(function () {
@ -120,9 +120,9 @@
});
}, converse));
}, converse));
}, converse, mock, utils));
}, converse, mock, test_utils));
describe("The Contacts Roster", $.proxy(function (mock, utils) {
describe("The Contacts Roster", $.proxy(function (mock, test_utils) {
describe("A Roster Group", $.proxy(function () {
@ -135,7 +135,7 @@
});
function _clearContacts () {
utils.clearBrowserStorage();
test_utils.clearBrowserStorage();
converse.rosterview.model.reset();
}
@ -146,8 +146,8 @@
spyOn(this.rosterview, 'update').andCallThrough();
converse.rosterview.render();
utils.createContacts('pending');
utils.createContacts('requesting');
test_utils.createContacts('pending');
test_utils.createContacts('requesting');
var groups = {
'colleagues': 3,
'friends & acquaintences': 3,
@ -243,14 +243,14 @@
describe("Pending Contacts", $.proxy(function () {
function _clearContacts () {
utils.clearBrowserStorage();
test_utils.clearBrowserStorage();
converse.rosterview.model.reset();
}
function _addContacts () {
_clearContacts();
// Must be initialized, so that render is called and documentFragment set up.
utils.createContacts('pending').openControlBox().openContactsPanel();
test_utils.createContacts('pending').openControlBox().openContactsPanel();
}
it("can be collapsed under their own header", $.proxy(function () {
@ -350,13 +350,13 @@
describe("Existing Contacts", $.proxy(function () {
function _clearContacts () {
utils.clearBrowserStorage();
test_utils.clearBrowserStorage();
converse.rosterview.model.reset();
}
var _addContacts = function () {
_clearContacts();
utils.createContacts().openControlBox().openContactsPanel();
test_utils.createContacts().openControlBox().openContactsPanel();
};
it("can be collapsed under their own header", $.proxy(function () {
@ -561,13 +561,13 @@
describe("Requesting Contacts", $.proxy(function () {
beforeEach($.proxy(function () {
runs(function () {
utils.clearBrowserStorage();
test_utils.clearBrowserStorage();
converse.rosterview.model.reset();
utils.createContacts('requesting').openControlBox();
test_utils.createContacts('requesting').openControlBox();
});
waits(50);
runs(function () {
utils.openContactsPanel();
test_utils.openContactsPanel();
});
}, converse));
@ -646,7 +646,7 @@
spyOn(converse, 'emit');
spyOn(this.connection.roster, 'unauthorize');
spyOn(window, 'confirm').andReturn(true);
utils.createContacts('requesting').openControlBox();
test_utils.createContacts('requesting').openControlBox();
var name = mock.req_names.sort()[1];
var jid = name.replace(/ /g,'.').toLowerCase() + '@localhost';
converse.rosterview.$el.find(".req-contact-name:contains('"+name+"')")
@ -661,10 +661,10 @@
describe("All Contacts", $.proxy(function () {
beforeEach($.proxy(function () {
utils.clearBrowserStorage();
test_utils.clearBrowserStorage();
converse.rosterview.model.reset();
utils.createContacts('all').openControlBox();
utils.openContactsPanel();
test_utils.createContacts('all').openControlBox();
test_utils.openContactsPanel();
}, converse));
it("are saved to, and can be retrieved from, browserStorage", $.proxy(function () {
@ -700,9 +700,9 @@
}
}, converse));
}, converse));
}, converse, mock, utils));
}, converse, mock, test_utils));
describe("The 'Add Contact' widget", $.proxy(function (mock, utils) {
describe("The 'Add Contact' widget", $.proxy(function (mock, test_utils) {
it("opens up an add form when you click on it", $.proxy(function () {
var panel = this.chatboxviews.get('controlbox').contactspanel;
spyOn(panel, 'toggleContactForm').andCallThrough();
@ -713,16 +713,16 @@
panel.$el.find('a.toggle-xmpp-contact-form').click();
}, converse));
}, converse, mock, utils));
}, converse, mock, test_utils));
describe("The Controlbox Tabs", $.proxy(function () {
beforeEach($.proxy(function () {
runs(function () {
utils.closeAllChatBoxes();
test_utils.closeAllChatBoxes();
});
waits(50);
runs(function () {
utils.openControlBox();
test_utils.openControlBox();
});
}, converse));
@ -739,11 +739,11 @@
describe("chatrooms panel", $.proxy(function () {
beforeEach($.proxy(function () {
runs(function () {
utils.closeAllChatBoxes();
test_utils.closeAllChatBoxes();
});
waits(50);
runs(function () {
utils.openControlBox();
test_utils.openControlBox();
});
}, converse));
@ -792,5 +792,5 @@
}, converse));
}, converse));
}, converse));
}, converse, mock, utils));
}, converse, mock, test_utils));
}));

View File

@ -1,13 +1,13 @@
(function (root, factory) {
define([
"mock",
"utils"
], function (mock, utils) {
return factory(mock, utils);
"test_utils"
], function (mock, test_utils) {
return factory(mock, test_utils);
}
);
} (this, function (mock, utils) {
return describe("Converse", $.proxy(function(mock, utils) {
} (this, function (mock, test_utils) {
return describe("Converse", $.proxy(function(mock, test_utils) {
beforeEach($.proxy(function () {
window.localStorage.clear();
@ -43,5 +43,5 @@
// Restore the connection
converse.connection = old_connection;
}, converse));
}, converse, mock, utils));
}, converse, mock, test_utils));
}));

View File

@ -1,13 +1,13 @@
(function (root, factory) {
define([
"mock",
"utils"
], function (mock, utils) {
return factory(mock, utils);
"test_utils"
], function (mock, test_utils) {
return factory(mock, test_utils);
}
);
} (this, function (mock, utils) {
return describe("The Converse Event Emitter", $.proxy(function(mock, utils) {
} (this, function (mock, test_utils) {
return describe("The Converse Event Emitter", $.proxy(function(mock, test_utils) {
window.localStorage.clear();
window.sessionStorage.clear();
@ -64,5 +64,5 @@
expect(this.anotherCallback.callCount, 3);
expect(this.neverCalled).not.toHaveBeenCalled();
});
}, converse, mock, utils));
}, converse, mock, test_utils));
}));

View File

@ -1,23 +1,23 @@
(function (root, factory) {
define([
"mock",
"utils"
], function (mock, utils) {
return factory(mock, utils);
"test_utils"
], function (mock, test_utils) {
return factory(mock, test_utils);
}
);
} (this, function (mock, utils) {
return describe("The Minimized Chats Widget", $.proxy(function(mock, utils) {
} (this, function (mock, test_utils) {
return describe("The Minimized Chats Widget", $.proxy(function(mock, test_utils) {
beforeEach(function () {
runs(function () {
utils.closeAllChatBoxes();
utils.removeControlBox();
test_utils.closeAllChatBoxes();
test_utils.removeControlBox();
converse.roster.browserStorage._clear();
utils.initConverse();
utils.createContacts();
utils.openControlBox();
utils.openContactsPanel();
test_utils.initConverse();
test_utils.createContacts();
test_utils.openControlBox();
test_utils.openContactsPanel();
converse.minimized_chats.toggleview.model.browserStorage._clear();
converse.minimized_chats.initToggle();
});
@ -26,7 +26,7 @@
it("shows chats that have been minimized", $.proxy(function () {
var contact_jid, chatview;
contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
utils.openChatBoxFor(contact_jid);
test_utils.openChatBoxFor(contact_jid);
chatview = converse.chatboxviews.get(contact_jid);
expect(chatview.model.get('minimized')).toBeFalsy();
expect(this.minimized_chats.$el.is(':visible')).toBeFalsy();
@ -37,7 +37,7 @@
expect(this.minimized_chats.keys()[0]).toBe(contact_jid);
contact_jid = mock.cur_names[1].replace(/ /g,'.').toLowerCase() + '@localhost';
utils.openChatBoxFor(contact_jid);
test_utils.openChatBoxFor(contact_jid);
chatview = converse.chatboxviews.get(contact_jid);
expect(chatview.model.get('minimized')).toBeFalsy();
chatview.$el.find('.toggle-chatbox-button').click();
@ -49,7 +49,7 @@
it("can be toggled to hide or show minimized chats", $.proxy(function () {
var contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
utils.openChatBoxFor(contact_jid);
test_utils.openChatBoxFor(contact_jid);
var chatview = converse.chatboxviews.get(contact_jid);
expect(this.minimized_chats.$el.is(':visible')).toBeFalsy();
chatview.model.set({'minimized': true});
@ -70,7 +70,7 @@
expect(this.minimized_chats.toggleview.$('.unread-message-count').is(':visible')).toBeFalsy();
for (i=0; i<3; i++) {
contact_jid = mock.cur_names[i].replace(/ /g,'.').toLowerCase() + '@localhost';
utils.openChatBoxFor(contact_jid);
test_utils.openChatBoxFor(contact_jid);
chatview = converse.chatboxviews.get(contact_jid);
chatview.model.set({'minimized': true});
msg = $msg({
@ -86,5 +86,5 @@
}
}, converse));
}, converse, mock, utils));
}, converse, mock, test_utils));
}));

View File

@ -1,13 +1,13 @@
(function (root, factory) {
define([
"mock",
"utils"
], function (mock, utils) {
return factory(mock, utils);
"test_utils"
], function (mock, test_utils) {
return factory(mock, test_utils);
}
);
} (this, function (mock, utils) {
return describe("The OTR module", $.proxy(function(mock, utils) {
} (this, function (mock, test_utils) {
return describe("The OTR module", $.proxy(function(mock, test_utils) {
beforeEach($.proxy(function () {
window.localStorage.clear();
@ -36,5 +36,5 @@
// Clean up
this.prebind = false;
}, converse));
}, converse, mock, utils));
}, converse, mock, test_utils));
}));

View File

@ -25,6 +25,7 @@
"strophe.disco": "components/strophe.disco/index",
"converse-dependencies": "src/deps-no-otr",
"jquery.browser": "components/jquery.browser/dist/jquery.browser",
"utils": "src/utils",
"moment":"components/momentjs/moment",
"converse-templates":"src/templates",
"tpl": "components/requirejs-tpl-jcbrand/tpl",

View File

@ -29,6 +29,7 @@
"ru": "locale/ru/LC_MESSAGES/ru",
"zh": "locale/zh/LC_MESSAGES/zh",
"jquery.browser": "components/jquery.browser/dist/jquery.browser",
"utils": "src/utils",
"underscore": "components/underscore/underscore",
"backbone": "components/backbone/backbone",
"backbone.browserStorage": "components/backbone.browserStorage/backbone.browserStorage",

View File

@ -29,6 +29,7 @@
"ru": "locale/ru/LC_MESSAGES/ru",
"zh": "locale/zh/LC_MESSAGES/zh",
"jquery.browser": "components/jquery.browser/dist/jquery.browser",
"utils": "src/utils",
"underscore": "components/underscore/underscore",
"backbone": "components/backbone/backbone",
"backbone.browserStorage": "components/backbone.browserStorage/backbone.browserStorage",

View File

@ -35,6 +35,7 @@
"backbone.overview": "components/backbone.overview/backbone.overview",
"bootstrap": "components/bootstrap/dist/js/bootstrap", // XXX: Only required for https://conversejs.org website
"jquery.easing": "components/jquery-easing-original/jquery.easing.1.3", // XXX: Only required for https://conversejs.org website
"utils": "src/utils",
"strophe": "components/strophe/strophe",
"strophe.muc": "components/strophe.muc/index",
"strophe.roster": "components/strophe.roster/index",

View File

@ -29,6 +29,7 @@
"ru": "locale/ru/LC_MESSAGES/ru",
"zh": "locale/zh/LC_MESSAGES/zh",
"jquery.browser": "components/jquery.browser/dist/jquery.browser",
"utils": "src/utils",
"underscore": "components/underscore/underscore",
"backbone": "components/backbone/backbone",
"backbone.browserStorage": "components/backbone.browserStorage/backbone.browserStorage",

View File

@ -5,6 +5,7 @@ define("converse-dependencies", [
"backbone.browserStorage",
"backbone.overview",
"jquery.browser",
"utils",
"strophe",
"strophe.muc",
"strophe.roster",

View File

@ -4,6 +4,7 @@ define("converse-dependencies", [
"backbone.browserStorage",
"backbone.overview",
"jquery.browser",
"utils",
"strophe",
"strophe.muc",
"strophe.roster",

View File

@ -6,6 +6,7 @@ define("converse-dependencies", [
"backbone.overview",
"jquery.browser",
"jquery.easing", // XXX: Can be removed, only for https://conversejs.org
"utils",
"strophe",
"strophe.muc",
"strophe.roster",

View File

@ -2,11 +2,12 @@ define("converse-dependencies", [
"otr",
"moment",
"locales",
"bootstrap", // XXX: Can be removed, only for https://conversejs.org
"bootstrap", // XXX: Only for https://conversejs.org
"backbone.browserStorage",
"backbone.overview",
"jquery.browser",
"jquery.easing", // XXX: Can be removed, only for https://conversejs.org
"jquery.easing", // XXX: Only for https://conversejs.org
"utils",
"strophe",
"strophe.muc",
"strophe.roster",

View File

@ -32,6 +32,7 @@ define("converse-templates", [
"tpl!src/templates/room_description",
"tpl!src/templates/room_item",
"tpl!src/templates/room_panel",
"tpl!src/templates/roster",
"tpl!src/templates/roster_item",
"tpl!src/templates/select_option",
"tpl!src/templates/status_option",
@ -73,11 +74,12 @@ define("converse-templates", [
room_description: arguments[30],
room_item: arguments[31],
room_panel: arguments[32],
roster_item: arguments[33],
select_option: arguments[34],
status_option: arguments[35],
toggle_chats: arguments[36],
toolbar: arguments[37],
trimmed_chat: arguments[38]
roster: arguments[33],
roster_item: arguments[34],
select_option: arguments[35],
status_option: arguments[36],
toggle_chats: arguments[37],
toolbar: arguments[38],
trimmed_chat: arguments[39]
};
});

View File

@ -0,0 +1,6 @@
<input class="roster-filter" placeholder="{{placeholder}}">
<select class="filter-type">
<option value="contacts">{{label_contacts}}</option>
<option value="groups">{{label_groups}}</option>
</select>
<dl class="roster-contacts" style="display: block;">

10
src/utils.js Normal file
View File

@ -0,0 +1,10 @@
jQuery.fn.hasScrollBar = function() {
if (!$.contains(document, this.get(0))) {
return false;
}
if(this.parent().height() < this.get(0).scrollHeight) {
return true;
}
return false;
};

View File

@ -1,6 +1,6 @@
// Extra test dependencies
config.paths.mock = "tests/mock";
config.paths.utils = "tests/utils";
config.paths.test_utils = "tests/utils";
config.paths.jasmine = "components/jasmine/lib/jasmine-core/jasmine";
config.paths["jasmine-html"] = "components/jasmine/lib/jasmine-core/jasmine-html";
config.paths["console-runner"] = "node_modules/phantom-jasmine/lib/console-runner";

View File

@ -1,5 +1,5 @@
(function (root, factory) {
define("utils", [
define("test_utils", [
'jquery',
'mock'
],