Commit 24d58a5b authored by JC Brand's avatar JC Brand

Merge branch 'consolidation'

parents a29a0293 e2d494f0
# This file must be used with "source bin/activate" *from bash*
# you cannot run it directly
deactivate () {
# reset old environment variables
if [ -n "$_OLD_VIRTUAL_PATH" ] ; then
PATH="$_OLD_VIRTUAL_PATH"
export PATH
unset _OLD_VIRTUAL_PATH
fi
# This should detect bash and zsh, which have a hash command that must
# be called to get it to forget past commands. Without forgetting
# past commands the $PATH changes we made may not be respected
if [ -n "$BASH" -o -n "$ZSH_VERSION" ] ; then
hash -r
fi
if [ -n "$_OLD_VIRTUAL_PS1" ] ; then
PS1="$_OLD_VIRTUAL_PS1"
export PS1
unset _OLD_VIRTUAL_PS1
fi
unset VIRTUAL_ENV
if [ ! "$1" = "nondestructive" ] ; then
# Self destruct!
unset -f deactivate
fi
}
# unset irrelavent variables
deactivate nondestructive
VIRTUAL_ENV="/home/jc/dev/converse.js"
export VIRTUAL_ENV
_OLD_VIRTUAL_PATH="$PATH"
PATH="$VIRTUAL_ENV/node_modules/.bin:$PATH"
export PATH
if [ -z "$VIRTUAL_ENV_DISABLE_PROMPT" ] ; then
_OLD_VIRTUAL_PS1="$PS1"
if [ "x" != x ] ; then
PS1="$PS1"
else
if [ "`basename \"$VIRTUAL_ENV\"`" = "__" ] ; then
# special case for Aspen magic directories
# see http://www.zetadev.com/software/aspen/
PS1="[`basename \`dirname \"$VIRTUAL_ENV\"\``] $PS1"
else
PS1="(`basename \"$VIRTUAL_ENV\"`)$PS1"
fi
fi
export PS1
fi
# This should detect bash and zsh, which have a hash command that must
# be called to get it to forget past commands. Without forgetting
# past commands the $PATH changes we made may not be respected
if [ -n "$BASH" -o -n "$ZSH_VERSION" ] ; then
hash -r
fi
...@@ -17,16 +17,17 @@ ...@@ -17,16 +17,17 @@
"backbone.localStorage": "1.1.7", "backbone.localStorage": "1.1.7",
"strophe": "git@github.com:strophe/strophejs-bower.git#v1.1.3", "strophe": "git@github.com:strophe/strophejs-bower.git#v1.1.3",
"strophe.roster": "https://raw.github.com/strophe/strophejs-plugins/b1f364eb6e854ffe844c57add38e885cfeb9b498/roster/strophe.roster.js", "strophe.roster": "https://raw.github.com/strophe/strophejs-plugins/b1f364eb6e854ffe844c57add38e885cfeb9b498/roster/strophe.roster.js",
"strophe.vcard": "https://raw.github.com/strophe/strophejs-plugins/f5c9e16b463610d501591452cded0359f53aae48/vcard/strophe.vcard.js", "strophe.vcard": "https://raw.github.com/strophe/strophejs-plugins/f5c9e16b463610d501591452cded0359f53aae48/vcard/strophe.vcard.js",
"strophe.disco": "https://raw.github.com/jcbrand/strophejs-plugins/75c8693992bc357c699b6d615eeb396e799f5c02/disco/strophe.disco.js", "strophe.disco": "https://raw.github.com/jcbrand/strophejs-plugins/75c8693992bc357c699b6d615eeb396e799f5c02/disco/strophe.disco.js",
"strophe.muc": "https://raw.github.com/strophe/strophejs-plugins/02310ad1b8da2962cd05b0f4bceaecca134efed4/muc/strophe.muc.js", "strophe.muc": "https://raw.github.com/strophe/strophejs-plugins/02310ad1b8da2962cd05b0f4bceaecca134efed4/muc/strophe.muc.js",
"otr": "0.2.12", "otr": "0.2.12",
"crypto-js-evanvosberg": "~3.1.2", "crypto-js-evanvosberg": "~3.1.2",
"almond": "~0.2.9", "almond": "~0.2.9",
"requirejs-text": "~2.0.12", "requirejs-text": "~2.0.12",
"requirejs-tpl-jcbrand": "*", "requirejs-tpl-jcbrand": "*",
"momentjs": "~2.6.0", "momentjs": "~2.6.0",
"jquery.browser": "~0.0.6" "jquery.browser": "~0.0.6",
"backbone.overview": "*"
}, },
"exportsOverride": {} "exportsOverride": {}
} }
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
* http://conversejs.org * http://conversejs.org
* *
* Copyright (c) 2012, Jan-Carel Brand <jc@opkode.com> * Copyright (c) 2012, Jan-Carel Brand <jc@opkode.com>
* Dual licensed under the MIT and GPL Licenses * Licensed under the Mozilla Public License (MPL)
*/ */
// AMD/global registrations // AMD/global registrations
...@@ -158,6 +158,7 @@ ...@@ -158,6 +158,7 @@
this.forward_messages = false; this.forward_messages = false;
this.hide_muc_server = false; this.hide_muc_server = false;
this.i18n = locales.en; this.i18n = locales.en;
this.no_trimming = false; // Set to true for phantomjs tests (where browser apparently has no width)
this.prebind = false; this.prebind = false;
this.show_controlbox_by_default = false; this.show_controlbox_by_default = false;
this.show_only_online_users = false; this.show_only_online_users = false;
...@@ -195,6 +196,7 @@ ...@@ -195,6 +196,7 @@
'fullname', 'fullname',
'hide_muc_server', 'hide_muc_server',
'i18n', 'i18n',
'no_trimming',
'jid', 'jid',
'prebind', 'prebind',
'rid', 'rid',
...@@ -863,8 +865,8 @@ ...@@ -863,8 +865,8 @@
is_chatroom: false, // This is not a multi-user chatroom is_chatroom: false, // This is not a multi-user chatroom
events: { events: {
'click .close-chatbox-button': 'closeChat', 'click .close-chatbox-button': 'close',
'click .toggle-chatbox-button': 'toggleChatBox', 'click .toggle-chatbox-button': 'minimize',
'keypress textarea.chat-textarea': 'keyPressed', 'keypress textarea.chat-textarea': 'keyPressed',
'click .toggle-smiley': 'toggleEmoticonMenu', 'click .toggle-smiley': 'toggleEmoticonMenu',
'click .toggle-smiley ul li': 'insertEmoticon', 'click .toggle-smiley ul li': 'insertEmoticon',
...@@ -892,13 +894,16 @@ ...@@ -892,13 +894,16 @@
this.model.on('showReceivedOTRMessage', function (text) { this.model.on('showReceivedOTRMessage', function (text) {
this.showMessage({'message': text, 'sender': 'them'}); this.showMessage({'message': text, 'sender': 'them'});
}, this); }, this);
this.updateVCard(); this.updateVCard();
this.$el.insertAfter(converse.chatboxviews.get("controlbox").$el); this.$el.insertAfter(converse.chatboxviews.get("controlbox").$el);
this.render().show().focus().model.messages.fetch({add: true}); this.model.messages.fetch({add: true});
if (this.model.get('status')) { this.render();
this.showStatusMessage(this.model.get('status')); if (this.model.get('minimized')) {
this.hide();
} else {
this.show();
} }
this.initDragResize();
if ((_.contains([UNVERIFIED, VERIFIED], this.model.get('otr_status'))) || converse.use_otr_by_default) { if ((_.contains([UNVERIFIED, VERIFIED], this.model.get('otr_status'))) || converse.use_otr_by_default) {
this.model.initiateOTR(); this.model.initiateOTR();
} }
...@@ -919,7 +924,7 @@ ...@@ -919,7 +924,7 @@
setTimeout(function () { setTimeout(function () {
converse.refreshWebkit(); converse.refreshWebkit();
}, 50); }, 50);
return this; return this.showStatusMessage();
}, },
initDragResize: function () { initDragResize: function () {
...@@ -937,17 +942,6 @@ ...@@ -937,17 +942,6 @@
this.scrollDown(); this.scrollDown();
}, },
updateUnreadMessagesCounter: function () {
/* If the chatbox is minimized, we show a counter with the
* number of unread messages.
*/
var $count = this.$el.find('.chat-head-message-count');
var count = parseInt($count.data('count') || 0, 10) + 1;
$count.html(count).data('count', count);
if (!$count.is(':visible')) { $count.show('fast'); }
return this;
},
clearChatRoomMessages: function (ev) { clearChatRoomMessages: function (ev) {
ev.stopPropagation(); ev.stopPropagation();
var result = confirm(__("Are you sure you want to clear the messages from this room?")); var result = confirm(__("Are you sure you want to clear the messages from this room?"));
...@@ -982,9 +976,6 @@ ...@@ -982,9 +976,6 @@
'extra_classes': msg_dict.delayed && 'delayed' || '' 'extra_classes': msg_dict.delayed && 'delayed' || ''
}); });
$content.append($(message).children('.chat-message-content').first().text(text).addHyperlinks().addEmoticons().parent()); $content.append($(message).children('.chat-message-content').first().text(text).addHyperlinks().addEmoticons().parent());
if (this.model.get('minimized') && (!msg_time.isBefore(this.model.get('time_minimized')))) {
this.updateUnreadMessagesCounter();
}
this.scrollDown(); this.scrollDown();
}, },
...@@ -1276,7 +1267,7 @@ ...@@ -1276,7 +1267,7 @@
converse.emit('onBuddyStatusChanged', item.attributes, item.get('chat_status')); converse.emit('onBuddyStatusChanged', item.attributes, item.get('chat_status'));
} }
if (_.has(item.changed, 'status')) { if (_.has(item.changed, 'status')) {
this.showStatusMessage(item.get('status')); this.showStatusMessage();
converse.emit('onBuddyStatusMessageChanged', item.attributes, item.get('status')); converse.emit('onBuddyStatusMessageChanged', item.attributes, item.get('status'));
} }
if (_.has(item.changed, 'image')) { if (_.has(item.changed, 'image')) {
...@@ -1285,61 +1276,53 @@ ...@@ -1285,61 +1276,53 @@
if (_.has(item.changed, 'otr_status')) { if (_.has(item.changed, 'otr_status')) {
this.renderToolbar().informOTRChange(); this.renderToolbar().informOTRChange();
} }
if (_.has(item.changed, 'minimized')) {
if (item.get('minimized')) {
this.hide();
} else {
this.maximize();
}
}
// TODO check for changed fullname as well // TODO check for changed fullname as well
}, },
showStatusMessage: function (msg) { showStatusMessage: function (msg) {
this.$el.find('p.user-custom-message').text(msg).attr('title', msg); msg = msg || this.model.get('status');
if (msg) {
this.$el.find('p.user-custom-message').text(msg).attr('title', msg);
}
return this;
}, },
closeChat: function () { close: function () {
if (converse.connection) { if (converse.connection) {
this.model.destroy(); this.model.destroy();
} else { } else {
this.model.trigger('hide'); this.model.trigger('hide');
} }
converse.emit('onChatBoxClosed', this);
return this; return this;
}, },
trimChat: function () { maximize: function () {
// TODO: Instead of closing the chat, we should add it to /* Restores a minimized chat box
// div#offscreen-chatboxes */
this.$el.hide(); // Hide it immediately to avoid flashes on the screen this.$el.insertAfter(converse.chatboxviews.get("controlbox").$el).show();
this.closeChat(); this.focus();
}, converse.refreshWebkit();
converse.emit('onChatBoxMaximized', this);
saveToggleState: function () { this.model.trigger('maximized', this.model);
var flyout = this.$el.find('.box-flyout');
if (flyout.hasClass('minimized')) {
flyout.removeClass('minimized');
this.model.save({'minimized': false});
} else {
flyout.addClass('minimized');
this.model.save({
'minimized': true,
'time_minimized': moment().format()
});
}
return this;
}, },
toggleChatBox: function (ev) { minimize: function (ev) {
var $target = $(ev.target), $count; /* Minimizes a chat box
this.saveToggleState(); */
this.$el.children('.box-flyout').attr('style', ''); this.model.save({
this.$el.find('div.chat-body').slideToggle('fast'); 'minimized': true,
if ($target.hasClass('icon-minus')) { 'time_minimized': moment().format()
$target.removeClass('icon-minus').addClass('icon-plus'); });
} else { this.$el.hide('fast', converse.refreshwebkit);
$target.removeClass('icon-plus').addClass('icon-minus'); converse.emit('onChatBoxMinimized', this);
$count = this.$el.find('.chat-head-message-count');
$count.html(0).data('count', 0);
if ($count.is(':visible')) { $count.hide('fast'); }
}
// Toggle drag resize ability
this.$el.find('.dragresize-tm').toggle();
this.setChatBoxHeight(this.height);
converse.emit('onChatBoxToggled', this);
}, },
updateVCard: function () { updateVCard: function () {
...@@ -1445,8 +1428,8 @@ ...@@ -1445,8 +1428,8 @@
hide: function () { hide: function () {
if (this.$el.is(':visible') && this.$el.css('opacity') == "1") { if (this.$el.is(':visible') && this.$el.css('opacity') == "1") {
this.$el.fadeOut('fast', converse.refreshWebkit); this.$el.hide();
converse.emit('onChatBoxClosed', this); converse.refreshWebkit();
} }
return this; return this;
}, },
...@@ -1460,13 +1443,16 @@ ...@@ -1460,13 +1443,16 @@
// Without a connection, we haven't yet initialized // Without a connection, we haven't yet initialized
// localstorage // localstorage
this.model.save(); this.model.save();
this.initDragResize();
} }
return this; return this;
}, },
scrollDown: function () { scrollDown: function () {
var $content = this.$el.find('.chat-content'); var $content = this.$('.chat-content');
$content.scrollTop($content[0].scrollHeight); if ($content.is(':visible')) {
$content.scrollTop($content[0].scrollHeight);
}
return this; return this;
} }
}); });
...@@ -1767,7 +1753,7 @@ ...@@ -1767,7 +1753,7 @@
} }
} }
if (!nick) { return; } if (!nick) { return; }
chatroom = converse.chatboxviews.showChatBox({ chatroom = converse.chatboxviews.showChat({
'id': jid, 'id': jid,
'jid': jid, 'jid': jid,
'name': Strophe.unescapeNode(Strophe.getNodeFromJid(jid)), 'name': Strophe.unescapeNode(Strophe.getNodeFromJid(jid)),
...@@ -1786,7 +1772,7 @@ ...@@ -1786,7 +1772,7 @@
className: 'chatbox', className: 'chatbox',
id: 'controlbox', id: 'controlbox',
events: { events: {
'click a.close-chatbox-button': 'closeChat', 'click a.close-chatbox-button': 'close',
'click ul#controlbox-tabs li a': 'switchTab', 'click ul#controlbox-tabs li a': 'switchTab',
'mousedown .dragresize-tm': 'onDragResizeStart' 'mousedown .dragresize-tm': 'onDragResizeStart'
}, },
...@@ -1890,8 +1876,8 @@ ...@@ -1890,8 +1876,8 @@
tagName: 'div', tagName: 'div',
className: 'chatroom', className: 'chatroom',
events: { events: {
'click .close-chatbox-button': 'closeChat', 'click .close-chatbox-button': 'close',
'click .toggle-chatbox-button': 'toggleChatBox', 'click .toggle-chatbox-button': 'minimize',
'click .configure-chatroom-button': 'configureChatRoom', 'click .configure-chatroom-button': 'configureChatRoom',
'click .toggle-smiley': 'toggleEmoticonMenu', 'click .toggle-smiley': 'toggleEmoticonMenu',
'click .toggle-smiley ul li': 'insertEmoticon', 'click .toggle-smiley ul li': 'insertEmoticon',
...@@ -1904,6 +1890,13 @@ ...@@ -1904,6 +1890,13 @@
initialize: function () { initialize: function () {
this.connect(null); this.connect(null);
this.model.messages.on('add', this.onMessageAdded, this); this.model.messages.on('add', this.onMessageAdded, this);
this.model.on('change:minimized', function (item) {
if (item.get('minimized')) {
this.hide();
} else {
this.maximize();
}
}, this);
this.model.on('destroy', function (model, response, options) { this.model.on('destroy', function (model, response, options) {
this.hide(); this.hide();
converse.connection.muc.leave( converse.connection.muc.leave(
...@@ -1913,9 +1906,13 @@ ...@@ -1913,9 +1906,13 @@
undefined); undefined);
}, },
this); this);
this.$el.appendTo(converse.chatboxviews.$el); this.$el.insertAfter(converse.chatboxviews.get("controlbox").$el);
this.render().show().model.messages.fetch({add: true}); this.render().model.messages.fetch({add: true});
this.initDragResize(); if (this.model.get('minimized')) {
this.hide();
} else {
this.show();
}
}, },
render: function () { render: function () {
...@@ -2387,7 +2384,7 @@ ...@@ -2387,7 +2384,7 @@
this.ChatBoxes = Backbone.Collection.extend({ this.ChatBoxes = Backbone.Collection.extend({
model: converse.ChatBox, model: converse.ChatBox,
comparator : 'time_opened', comparator: 'time_opened',
registerMessageHandler: function () { registerMessageHandler: function () {
converse.connection.addHandler( converse.connection.addHandler(
...@@ -2478,43 +2475,67 @@ ...@@ -2478,43 +2475,67 @@
} }
}); });
this.ChatBoxViews = Backbone.View.extend({ this.ChatBoxViews = Backbone.Overview.extend({
el: '#conversejs',
initialize: function () { initialize: function () {
var views = {}; this.trimmed_chatboxes_view = new converse.MinimizedChatBoxesView({model: this.model});
this.get = function (id) { return views[id]; }; this.render();
this.set = function (id, view) { views[id] = view; }; this.model.on("add", this.onChatAdded, this);
this.getAll = function () { return views; }; this.model.on("maximized", function (item) {
this.trimChats(this.get(item.get('id')));
}, this);
},
this.model.on("add", function (item) { render: function () {
var view = this.get(item.get('id')); this.$el.html(this.trimmed_chatboxes_view.render());
if (!view) { },
if (item.get('chatroom')) {
view = new converse.ChatRoomView({'model': item}); _ensureElement: function() {
} else if (item.get('box_id') === 'controlbox') { /* Override method from backbone.js
view = new converse.ControlBoxView({model: item}); * If the #conversejs element doesn't exist, create it.
view.render(); */
} else { if (!this.el) {
view = new converse.ChatBoxView({model: item}); var $el = $('#conversejs');
} if (!$el.length) {
this.set(item.get('id'), view); $el = $('<div id="conversejs">');
$('body').append($el);
}
this.setElement($el, false);
} else {
this.setElement(_.result(this, 'el'), false);
}
},
onChatAdded: function (item) {
var view = this.get(item.get('id'));
if (!view) {
if (item.get('chatroom')) {
view = new converse.ChatRoomView({'model': item});
} else if (item.get('box_id') === 'controlbox') {
view = new converse.ControlBoxView({model: item}).render();
} else { } else {
delete view.model; // Remove ref to old model to help garbage collection view = new converse.ChatBoxView({model: item});
view.model = item;
view.initialize();
} }
this.trimOpenChats(view); this.add(item.get('id'), view);
}, this); } else {
delete view.model; // Remove ref to old model to help garbage collection
view.model = item;
view.initialize();
}
this.trimChats(view);
}, },
trimOpenChats: function (view) { trimChats: function (view) {
/* This method is called before a new chat box will be opened. /* This method is called before a new chat box will be opened.
* *
* Check whether there is enough space in the page to show * Check whether there is enough space in the page to show
* another chat box. Otherwise, close the oldest chat box. * another chat box. Otherwise, close the oldest chat box.
*/ */
var toggle_width = 0, if (converse.no_trimming) {
return;
}
var toggle_width = 0,
trimmed_chats_width,
boxes_width = view.$el.outerWidth(true), boxes_width = view.$el.outerWidth(true),
controlbox = this.get('controlbox'); controlbox = this.get('controlbox');
if (!controlbox || !controlbox.$el.is(':visible')) { if (!controlbox || !controlbox.$el.is(':visible')) {
...@@ -2529,16 +2550,31 @@ ...@@ -2529,16 +2550,31 @@
if (this.model.length <= 1) { if (this.model.length <= 1) {
return; return;
} }
if ((boxes_width + toggle_width) > this.$el.width()) { trimmed_chats_width = this.trimmed_chatboxes_view.$('.box-flyout').outerWidth(true) || 0;
// trim oldest view (which is not controlbox) if ((trimmed_chats_width + boxes_width + toggle_width) > this.$el.width()) {
this.get(this.model.at(1).get('id')).trimChat(); this.getOldestMaximizedChat().set('minimized', true);
} }
}, },
showChatBox: function (attrs) { getOldestMaximizedChat: function () {
// Get oldest view (which is not controlbox)
var i = 0;
var model = this.model.sort().at(i);
while (model.get('id') === 'controlbox' || model.get('minimized') === true) {
i++;
model = this.model.at(i);
}
return model;
},
showChat: function (attrs) {
var chatbox = this.model.get(attrs.jid); var chatbox = this.model.get(attrs.jid);
if (chatbox) { if (chatbox) {
chatbox.trigger('show'); if (chatbox.get('minimized')) {
chatbox.set({'minimized': false});
} else {
chatbox.trigger('show');
}
} else { } else {
chatbox = this.model.create(attrs, { chatbox = this.model.create(attrs, {
'error': function (model, response) { 'error': function (model, response) {
...@@ -2550,6 +2586,126 @@ ...@@ -2550,6 +2586,126 @@
} }
}); });
this.MinimizedChatBoxView = Backbone.View.extend({
tagName: 'div',
className: 'chat-head',
events: {
'click .close-chatbox-button': 'close',
'click .restore-chat': 'restore'
},
initialize: function () {
this.model.messages.on('add', function (msg) {
this.updateUnreadMessagesCounter(_.clone(msg.attributes));
}, this);
this.model.on('showSentOTRMessage', this.updateUnreadMessagesCounter, this);
this.model.on('showReceivedOTRMessage', this.updateUnreadMessagesCounter, this);
this.model.on('change:minimized', this.clearUnreadMessagesCounter, this);
},
render: function () {
var data = this.model.toJSON();
if (this.model.get('chatroom')) {
data.title = this.model.get('name');
this.$el.addClass('chat-head-chatroom');
} else {
data.title = this.model.get('fullname');
this.$el.addClass('chat-head-chatbox');
}
return this.$el.html(converse.templates.trimmed_chat(data));
},
clearUnreadMessagesCounter: function () {
if (!this.model.get('minimized')) {
this.$el.find('.chat-head-message-count').html(0).data('count', 0).hide();
}
},
updateUnreadMessagesCounter: function (msg_dict) {
var count, $count;
var msg_time = (typeof msg_dict === 'object' && moment(msg_dict.time)) || moment;
if (this.model.get('minimized') && (!msg_time.isBefore(this.model.get('time_minimized')))) {
$count = this.$el.find('.chat-head-message-count');
count = parseInt($count.data('count') || 0, 10) + 1;
$count.html(count).data('count', count);
if (!$count.is(':visible')) { $count.show('fast'); }
}
return this;
},
close: function (ev) {
if (ev && ev.preventDefault) {
ev.preventDefault();
}
ev.preventDefault();
this.$el.remove();
this.model.destroy();
converse.emit('onChatBoxClosed', this);
return this;
},
restore: function (ev) {
if (ev && ev.preventDefault) {
ev.preventDefault();
}
this.$el.remove();
this.model.set({
'time_opened': moment().format(),
'minimized': false
});
return this;
}
});
this.MinimizedChatBoxesView = Backbone.Overview.extend({
initialize: function () {
this.model.on("add", function (item) {
if (item.get('minimized')) {
this.addChat(item);
}
}, this);
this.model.on("change:minimized", function (item) {
this.onChanged(item);
}, this);
},
render: function () {
return this.$el;
},
_ensureElement: function () {
/* Override method from backbone.js
* Make sure that the el and $el attributes point to a DOM snippet
* from src/templates/trimmed_chats.html
*/
if (!this.el) {
var $el = $(converse.templates.trimmed_chats());
this.setElement($el, false);
} else {
this.setElement(_.result(this, 'el'), false);
}
},
onChanged: function (item) {
var view;
if (item.get('minimized')) {
this.addChat(item);
} else {
view = this.get(item.get('id'));
view.restore();
}
},
addChat: function (item) {
var view = new converse.MinimizedChatBoxView({model: item});
this.$('.box-flyout').append(view.render());
this.add(item.get('id'), view);
}
});
this.RosterItem = Backbone.Model.extend({ this.RosterItem = Backbone.Model.extend({
initialize: function (attributes, options) { initialize: function (attributes, options) {
var jid = attributes.jid; var jid = attributes.jid;
...@@ -2580,7 +2736,7 @@ ...@@ -2580,7 +2736,7 @@
openChat: function (ev) { openChat: function (ev) {
ev.preventDefault(); ev.preventDefault();
return converse.chatboxviews.showChatBox({ return converse.chatboxviews.showChat({
'id': this.model.get('jid'), 'id': this.model.get('jid'),
'jid': this.model.get('jid'), 'jid': this.model.get('jid'),
'fullname': this.model.get('fullname'), 'fullname': this.model.get('fullname'),
...@@ -2963,17 +3119,11 @@ ...@@ -2963,17 +3119,11 @@
} }
}); });
this.RosterView = Backbone.View.extend({ this.RosterView = Backbone.Overview.extend({
tagName: 'dl', tagName: 'dl',
id: 'converse-roster', id: 'converse-roster',
initialize: function () { initialize: function () {
var views = {};
this.get = function (id) {
return views[id];
};
this.set = function (id, view) { views[id] = view; };
this.model.on("add", function (item) { this.model.on("add", function (item) {
this.addRosterItemView(item).render(item); this.addRosterItemView(item).render(item);
if (!item.get('vcard_updated')) { if (!item.get('vcard_updated')) {
...@@ -3007,7 +3157,6 @@ ...@@ -3007,7 +3157,6 @@
}); });
} }
this.$el.hide().html(roster_markup); this.$el.hide().html(roster_markup);
this.model.fetch({add: true}); // Get the cached roster items from localstorage this.model.fetch({add: true}); // Get the cached roster items from localstorage
}, },
...@@ -3029,7 +3178,7 @@ ...@@ -3029,7 +3178,7 @@
addRosterItemView: function (item) { addRosterItemView: function (item) {
var view = new converse.RosterItemView({model: item}); var view = new converse.RosterItemView({model: item});
this.set(item.id, view); this.add(item.id, view);
return this; return this;
}, },
...@@ -3460,7 +3609,7 @@ ...@@ -3460,7 +3609,7 @@
if (converse.show_controlbox_by_default) { if (converse.show_controlbox_by_default) {
toggle.hide(); // It's either or toggle.hide(); // It's either or
} }
$('#conversejs').append(toggle); $('#conversejs').prepend(toggle);
return this; return this;
}, },
...@@ -3503,14 +3652,17 @@ ...@@ -3503,14 +3652,17 @@
} }
}); });
this._initialize = function () {
this.chatboxes = new this.ChatBoxes();
this.chatboxviews = new this.ChatBoxViews({model: this.chatboxes});
this.controlboxtoggle = new this.ControlBoxToggle();
this.otr = new this.OTR();
};
// Initialization // Initialization
// -------------- // --------------
// This is the end of the initialize method. // This is the end of the initialize method.
this.chatboxes = new this.ChatBoxes(); this._initialize();
this.chatboxviews = new this.ChatBoxViews({model: this.chatboxes});
this.controlboxtoggle = new this.ControlBoxToggle();
this.otr = new this.OTR();
if ((this.prebind) && (!this.connection)) { if ((this.prebind) && (!this.connection)) {
if ((!this.jid) || (!this.sid) || (!this.rid) || (!this.bosh_service_url)) { if ((!this.jid) || (!this.sid) || (!this.rid) || (!this.bosh_service_url)) {
this.log('If you set prebind=true, you MUST supply JID, RID and SID values'); this.log('If you set prebind=true, you MUST supply JID, RID and SID values');
......
/** Converse.js (Web-based XMPP instant messaging client)
* http://conversejs.org
* Copyright (c) 2012, Jan-Carel Brand <jc@opkode.com>
* Dual licensed under the MIT and GPL Licenses
*/
/*!
* Converse.js (Web-based XMPP instant messaging client)
* http://conversejs.org
*
* Copyright (c) 2012, Jan-Carel Brand <jc@opkode.com>
* Dual licensed under the MIT and GPL Licenses
*/@font-face{font-family:Converse-js;src:url(fonticons/fonts/Converse-js.eot)}@font-face{font-family:Converse-js;src:url(data:application/font-woff;charset=utf-8;base64,d09GRk9UVE8AAEacAAsAAAAAfSwAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAABDRkYgAAABCAAAQlYAAHXHpo6K0EZGVE0AAENgAAAAGgAAABxl7tJoR0RFRgAAQ3wAAAAdAAAAIACEAARPUy8yAABDnAAAAE0AAABgU5fcYmNtYXAAAEPsAAABPQAAArgwQqoFaGVhZAAARSwAAAAsAAAANv3EpBNoaGVhAABFWAAAAB4AAAAkBHEAXmhtdHgAAEV4AAAAOAAAASh5QAIcbWF4cAAARbAAAAAGAAAABgBXUABuYW1lAABFuAAAANYAAAGnabUpXnBvc3QAAEaQAAAADAAAACAAAwAAeJzVvQl8zUf3Bzy/5N6bm0giyI09scR67Qm11U6RLkqqHltRFLG0kaK2aqtor6KEaqmqKiqqqKq9ategllBLitolliByr9zIvN/vzM0Vbfr8+/zf93k/n399eu78Zj1z5syZc2bOTAxhMgnDMAJbjxg+akD8yAE1h4wUho8wRDPXGz6ueF9XCZMj0NcRaAoPECW7FJUOhzcQ6PfhEFeKK9Fcxndm4TJChJQxNS1SRgSVGXKwqOjGOqyisAgTZUWkqCmaiFaig+gkXha9xQAxVIwUY8U7YpqYKeaJz8VSkSTWiU1ih9gnDosTIlVcEmnirnAJaViMICPUKG1UMKoZ9Y2mRlvjWaOL0d3oawwyRhijjAnGZMNhzDY+NRYby43Vxg/GVmOX8Ytx1DhlnDeuGreMTMPt4+Pj7xPiU9wn3KeSTw2fKJ/GPi192vu84NPVp5dPf584n1E+77w5fHC7OnXq4Ket/qn3VJ22+iuKXw3qNhg8fNSrg+NfHTpg2JsjB7/ad+jwEQkD+vcbqlIbtFY/ddrpn7a6AvX1VF1PdY1UdXXr8at+u9b6p5WOrK9/Guifp/RPS/3jydKGBaI8X/Xq6p96+ida/+ha6ula6ula6jXUP7r1errOep5a2umfNvpH9zdKEyFKtxClW4hSRHiqQTSIMHLY4KEDBvZ9dYCmS5v838jZUEVH6faiWiqsGzTSPw31T7QuqVuP0j/RutnoujqL/qqr6dnSk6b7F637F637F637F63bi9b9i9b9i26tf3T/onX/onV79XWd9aP0T309VJ4fXWd9z09LHdlSY60QbKWoi5+G+qeB/tEI1te41NE56+rBqaMJCZwez7b8E08IY6oxzfjA+BAMPd34yJhhzDRmGR+DuecYicZcY57xiTEfjP6ZscBYaHxuLDK+ANN/aSwxvjKWGl8byzABVhjfGCuNJGOV8S0mw3fGGmOtsc743liPibHB+NHYaGwyNhtbMEm2GduNn4wdxs/GTkyY3cYeY6+xz9hvHMDkSTYOGoeMw8avxhFMpGPGcSPFOGGcNH7DpDptnDHOGqniZc5xHxEkeog7PqV9lvommoZahlmOWj/w/7bQD4GfBV4JvBV8pGjDojGhjcJyS39VtlBEaoSzcqsqM6t2rxZfvYX9xZodaw6v+Wntk3UH1p0b9X2DyQ2Tm01q0aLFLy0TWgW26tHqfpuKbeLavtc2o8PuZ7s+t6zTB52+fTGt869dkmPvvFT8pXov7X0p5aV7XYu/bH15w8u//eu77kV6DOj9zCvPv5LSJ7pPTt8yfav2bd73y76r+x7q17rf4QGDBpwLzhnsSjDlJLoS/YJdiVJs3Ge3OaR40dTDja+9G/bjRzbqMMYh5Wp3RaeKO2B2xkrZ4t3nbe5YZ6Q71h3pQCDU4XCGEroJzcGuNq4Em5S3HtVB2Tt9f5Eyu9AUKe99aZHyQeZYa1yc0+Q2JSWZpQhAggjqnS2FOXOsFCGLRgGFoj6zAGvUOiNlaq35UlQ8WlnK40cHOaw5kTkmm3Mr/qEC91b8i4wzS2PAgRxpdGxgQqOXh1qlKD3H5U5wJ6iAijIHO2s429ikaPR6eSlqzbxAcJ6f5TyAcfIoUuVeJGhwFAl5cRe8ceW9ZcuHO1NVrUU+/UyK4IEDpbyLkLw9cGCEe72fCnii8uLDc2It+eJVIRaPcCL/43q88eFOk2VO0pykJIfVGWpJcsyJmxNnRdePodXnh3WSovnsNQT3+BkMMPSQlDsYaj4nSsq1SJBrGdqhEgjW5iXoLDs8xSKcCc54W1JMnF+wM9Z52VYuwB0bVi7AFeqOtjlDzzpAUMAe5gJ75U75m15FF9yrLQ5nrOOsw1wg6WLcobZ0d6LdL1gaafNDpDHzXi10N2h3CSl/qVhUCr+vTkm5e/xtcNfPw44RDg+V0tWzKmNXgdGaXGdOcOpG8tCDVdOlXLNsBdiwbR8pV81e4yCs6WDscnDe5sXM2RXc2CUBpZtWl6J+6irWOESKJu/FghmbfXyTcHYttj6PscAjaNdvzHkbHPfM0ZUsXk+KF/5VXFcpugwPRZkuwzo5GPuZbl48c+QyUTpslbknBtWTuRlBx2zO6HR3dDqYNNoVanPYgVi3Z62g9N2Rb0phugPeu7O5txQ+pMDtYzUdVil3Rv4u5aGzFTlFTnN2VNFA1GMC48TTSNWAcZhFVTipTkc4nJOdmBtPF38WDfWI3qoaSmBD570NlWRDtVRDlcLR0JmLf22ICbohpGrAuPwNLXUkO5LMwelSbD/bH2NoblxGCvHTBIReLoTQgAiEhp92WBdIGSG3jwMXZX9cBWhlr3igk2X2nsuqiNW5XopzZ5fZFlhQZkQpENXcFaJFfvaHrhdy6T3gW6FDQz9dihEIfdNAZ5LZs646rOMsEFWJEHTyj45vI26eScqLM7pKmRWG0OUfn5XCf+sQ0GFgJVT7yQwpb646hNzpPW8BHu+8RsorJ6dJeZpi4Uy1MaCT+RIY4Jfh88HqASH9pHQ3LIJKGs2X8uGOllJYK4JbXRBEwkc2Ba8vrCqFwelXuLwfOlH47SYoPm5DOykXAIj+HTZLOdkLFuQl8DMcKGRM2oPynLkZ5x6iztxsDN+rp9jOXDAnxZ9/47JSWJpvJz6QVqZ9GaBL/Y8H+RHjzZhz2WXZCzRWeeZ5StpvyaKhXxVljz+SoliXaFIhDvL407OaMqJ0+/WaWqLsGyuRO/yHd9DSR/1mSRH/+eseoD69cZIhiVB4sDPTGW1LSkq3O+IccfYYhyMGPZnYQkjRcofA/AUQzVo8EdrO1Hd2CHCQIzk9yZHkSLfHxWHtiHOwkqS8f0hwJCUlWSEsctb0k9Ca92AZyEnHulL0ffCP66np6FE2pvi1sGlSlHx7AoiB6sFUYjJY/8yXH0pR4RzWiX3xNaQo8/ZEKaf6oAvi89fRQ+E7D2STXVxAWL78LEDuRoDYraxkIkO5rOldpgYgZLTXudlITlU/1jUTQVJiX3xNKS9NmqzblL+Xt3gwwX/jzcRvqpRXJ7Uizr+C78x92I/3sZCOwzR3g3nk5VbPS/loACbemULvSxH4+qsY/LfCMHizh6MC9FK82z7LgfGpU8gHsEQ9yP+02SPAZr9nMldNxJkvIdQUoq34lx9IEfmMHWj3e16Kqi//yg4grmq3EHZ0NFLbpSH0+TpdQl7vPdrqbaw3q8YwZ5RPBnGuHQoG/DUzV6PAXDXY2BKiuRR8PwU1PeqfKkUlrPTozkQw1bbvrcGuBGemLS4uOYZLSJcN6NCqjh6Az/B/k+IOzYlm0oBLiP2sbL51xxWr1p1U77oTqdcdV/Tf1ubGciFP7ATvpG7HCF+Ke59giueTQJRJHKcTRMWW7T2gTOJYnfA4ThVTCSwW4Y6WuYurN7O5YuNyYv2c69NtSUCuYrcTyPgvDF2Z5XZkXJYWHuMAZqnLhyG+2xyCE/y0R8RxIfy5VqgrlqoGFjMRVAmscW/BdYV5RE60n6dH986AzTO2ruJnQrjMnXj0ZjJUllqhqNgZFQ2wCN2wVjgKcPBQBPGwHjwI0BeoW1rPU5+Q4LWO3kwqoBzXXuvB6f9xuf9texWOMMPhf1su2ZKeE2tzRcdAWhWpFapyHkYmM+bIQ1LCWe8eKMh66yEmG+uY8/c66hM5bh+9qctZVM9Yrs9fykV5y9X+/6KcRRWk7vMflwx29XdFgwlKkalt1JfSqDldAce7U/xUwBOVFx+uZog3XhVi8Qgn8j+uxxsPon7SG/xe40g/zIQxX4LeH/cRmNjmmseBcYk6mMk5Xx4EDst/7ekuqn6cRSG8Ik+8I+VrlVeg1LxN3VD0dDdpNGqd6OB3CSmmVob+dfBr9GT/6W6oqSGrbspWxFQ2OO+j45AZ2cdqQgtaCvPBOdk9uUePHlvcoW6Tw22KwhSPWVvFjWX+5vOnKeCKra3ihDKx/vnTUZecJofT5AztscXsTjAhZIpLgo5sQlWJmDTlA/gbipZKj56BkqWmYOK4k9qgO2cgw5xv7oQo7BGJ0ENK0oOQypnLh2OUvmkI+QC9UZQv3R9ELHkGS0oThH4fVA/dSrXtVHAXBPTuhYh97Y6U5y4jdKUU1tfbXfezNIYhPcrFGr+D+FyaqluR1yotYcug2ZW3ShAbSJvL0+K5bDzoOQT1PuiwBcL4204IvReLBf+lFpCez32MOMrmR8/NRsjug1CnKzSyKs4j/A4K3EMuTfdvdiCA3pszohdBT4Ce1QBWIV9O698ArkHsyH1DwYEm4CxHLEIoCO3Jkp9CD3h0n63AwCjysDhwOABsClPiZxwDZfxzs4FoGtWZoBPN0deLM5GRKGZ+8ROI9GAnQk5Mw6KvFoPW8FoGhuvDp7kmVK4EWDGtmaakqPDJYU1dUepqZU1xEfyv+XoURKFunTkyUGCtljQ9WsL6IcqWmoo1y4SOYkSfdViTTRjiLBf0w7JDdtCGta6FNm5cx4pnbYZFzHfweGhGg9+CPpO8G3VDoRb+f0CelPyiMXKXXjUdjbVbC8X2p6mgR6PnoeeuGZPoAYyTe5m6iQkEogUTGCdemDoUcyb4VRoJQZeghhmkRVAZLK++E77HxPrunJQ3yv8CjSkJK/9dalUVDCu11/cSCYc+y+UXZcydYIaKzWWBi58rBLP5li9sbv+vukCu5wCtYusbIX/YDgHLoQuYy+jyKN72v6XZ/34ulBp6EEM/dTWIeWcR2HdxC6C+6yTWKyd6UrwBtOhcKqFhr0BfyY6HVhg6HUts1m8gssyhGvz6SMyHGVuWSdH9eSjJCXv6OJSa6PBojEsuUOB8VI5w+ybIpUpm6HF7fn4GQ3MFcuD0ufog1PeoxP0idGnRFxLO3aAtcrsjJgD+Ti6+2AZM/UdcG3xuwZS908CE5u99uANSr/moqRimeNjNLZdgZfJPxZA0eFTbAwKh9shcLsxBnaDiy/LJCM3qRyXtJ2syhuVBH4MaH0yLB5wf4tsXEJoCyhix2/7xHN3mnaOOfztHa3jn6IL/N3O0hXeONvfO0V15c7Tf0X8+R63BWc7JEM8VApzzZO6zK+rZKgaoKC5LJV9qQZ0zIReYW7CyhF0Fc7mqgT9to2AyuZbDsC5WojNQpfAPqbsSI3L5HGaGGcbatfXIFzItXsoLV8qBAOeP9NMQNBsNM+zCZdi1f2SXYU6oYXcOw2a7dwWMcfPaatYIJkj7YAxbgQJ1oySEqqvqA2R2+BAbLD5XV+6geM2qUx71ZoWTh74Ai2a9eBND1wh2zqMXziOOOv6jTkvzhq5LAget1mlC8KJ82AtL8v3baPx+NmzK3OVoPHdFFsCySgDkytyv5gJsg2Ysyb2+vtQsyWm+PkEITXfQasOS96gDJFIR6v8PdryHoXuUiaHbh4nl774ORG9ySyEw8REi+4NIhTdjUXmwog6K2LFYZBUF/4a8xV0F5wzEjV2MaovkbgQsV3sRKQlxGFG6AqkLFbxEzBRNcRFU6oIeBRHAbTfbKJiBft1OcrRuIHRyPzK/iTnt+/AzjmhzitcnRtvVNI67MdR2A6gVK4DPCDRdYTgIc3c0BEG1pL0g/vLb4KTeMMDTKUTKziyFOAMKUFgLWDlXxG5ON5QudBXcEnAMZkpAc5gFQXWuQGiR4woHdoRgK1EeNonlPDi59OUFaKXMV6cAqxw6Rg5A5yotwAy/Xh+iq8w4cNy16jvQ1hYIsXMLIYZDXgGFU6PHc/BTo8c5dLS8sxgoXLP7QlS0DWdxrMWXJpdilZAxZ/p3R8ZT9YYDXupZlc1PwApdegAE1rVzozV6MuP+BqKMlHu/lmY3wAIPan6suybv0xa8ZkBk3PhpO0mAdeTi61dJFjBN6pfTNKnkydjGlFofr6SEyqOofEACg6xYXUr7v7UFJDd9jsEvBk1B+H8dgZF7H10PipiAOFr5oZtfwViXAcFLpqLXxdaBP0o1wTpgXoZxLQSpIXz6Qpb5Xe6OCvZcAgULPT8fgrpIpT7ch7BiTkp3L6yZflkHMHnehoVsatMdKLeC4AhJ+xkcuX8kiP0UV4+qq0NRtFE3rFCV6g6CJo7OiZrcPan54wzGVYY+342L5PKh3LX47cV0lk8AZVEJ6jyCEbC9w3bGo9kt13Xb0mWF6SidvTeCFrfPLAZiMnPtYGKNCZ3FfY1CQbMxdRbVZ+8qgPspqUumgrQ3KQlDN0NA/HGpGCgTno24O+dJLehaaWOxEJoW3kZc7DgYneP/DzHxUC8Tj9VMPFYxcdz/j0xs8TLxsP+jTDzLy8TxBTJxZS8Tz/g/wsQyd+78kCQYXO6mVYHIeuj9jyrO49KI8ZWti2Jox68CCtsOz7foQdHj8WdeF8FcK8jrogS1R/K6CO02R/O6KJGbrXldlJJNNK/LzJKp5PU1nqUrCGahdF5eoHgdTcmfyO8fYrUst4f8/lk+fp/NJagM+R0KYPFfmpHfryI7dBTF76L8q3M1v4uKye+D30XRYzV5KFQFnOeGJiBC5s9Ba/2eR0+T9yFkg7VxtzDUqszQsWj8WE180hbJyWqLVZzqas6NHIQawfjKOU8N8Vj7kYBnDp9W4+tQA6zGF6pAsac+4vjSmNj2Bse3OQaOm3IcX2FaEvi34ytdpa/p8ZWZ90bo8ZVXdv+ux1derThXj69Mb7tEj6+894dFj6/M6n5Rj690Ux9TQgqq8iThTMQIZ/vxUCCbNcgpNxB6JoNKIFZo0wF0zTIJg2B+AYNloYFuPlqVAGI5vOGLBBjUqjxCCVm6UIrqW1c5lCL71/1lTEgxSYwz6+MFfZRwyHuK8PgoQQEdd+YPT5Z6lSJUsXDnenQ225KmED7pRXh1AQiX8SI8yItwZy/Cof85wuqsQuP1xJHEnxHWCRHqdCM82Bmr1NeKXWCclMGM0UB9euPkJYRkKkOp3s8C4rxl0erUXaegve86ZaPxkOutLOBxJkcezGvCoarivrCLm8f/uKluFAFj2ghbZIBqEh9Nqynr8BHmd1cTAU/bvsnxAMbJu9+4uVFhAtVk9pt+KszYHA9Qn944XQ9Kh7N2qFD7DvSz5avOm/5kEzneJnIq/2dNgIsbtvClQS98c8xPduZ/aMnkRxstChEvHfM2l8LPow6Vy/G4QYeqxTpOGoWnBajWTBfQWqmJVI/fsXiA+vTGyTSE5JV3LGzNXU611gwRk/Z7ktIm7lefDpXLoWNZ1KFqwRAe3zWPnZwPEhY7D8EaUm43wR5+NvUAxsk7SJU3kaDBHSTkxe32gDs6syrbJDxYGhmQvqLGc51slcAPHTgFAj8KtlUOgEz5gSvrqIfdbPpASmbzPNPnVZib2a0CYQr9gAXtoaszWasQLKDsqusd0DRSM7cwnIWplttxosoBCbcQ5mp2y18QEntY0ymeDtAQulQcUvTGKxMImvAT2lTo15fMujlk383mnvdU8dAVwmrftqom2FhunEM1DSQCdph1DqD2ri7lQVchLptYdf0yvXtZ3aYIW7xH4yHKXB9p1s0Jc88hujlh3papmoP08VttVc0h8rdnHUm6zwybNSWYQzcnzNsbeI7wzD2rolT6v86y/nhPc2GLd/NzPRNSzbo5ZG/M5tZ5qrD4ZahqrWwiQDWW5FDNAYkHLczMcceDmnnb93noKsTFHquun3Rkc+8p2qJ3X0+AqbkwPFqdDqC64C5vEmzgZ0cN5F0eGzCOJwMewDjv+YcGyJKg43jQEOGKdE6G8Dr3AvSWhxcE2pJqs+UhVlIZDZ0nx4rRkkGYYjklQojfPugwt0Zi3t3/GRkvck/j3NQaWEGbXOM4QG2TOQ6Yr4+ojeR83QehzzEYOdt5rlRsErjiUkojdqxpHuekNGbCHjMz0Wj+/AELhudV4ZiiquWmGU/9z021s9lFHgRujYwmUsNYuvgRoppCpNcT/XHoCHdL0CWM5sPz29nR0uQ7hkX5tVXMzMED60UPdSkhkt9nV7E8iXs1UepW/D2g2HAO2/wcc6xZtuoqSo85YdbYCZ/Sy4kxliofHg6xF8KnX6DV09WbbzfJz7XFeLxbxr7PrCkjfFt+wK5yF7P7Yk1B4ZsA5U931cWTenZVlJ3VT3UVmtL0Q2bdVeF77bbuqvB1PqtHT5jMl62qq+C2coI8X34tbYNybczsKnpgskTqrqJUgOoqa+IhMruK+g96uqraRFeJRwkzu/o1sRvPrn5IjC+orrIX7a26q+jbXs29AJNVV5nQ1Kwy5WWHMuSpImEsq11mVU2wsRRPswqBotOnE6nZLH29DlGtlYd08mTVVdUlK7sHBbb8mqucb+gqCVDGzBwXPWTRBAJTs6uKqa2erpKp0SZMpfs7dms8yNRm3VUMSQlivEhzJHrxoRo6q6erZGolHDDI41VXFVObdVc97MAyZGp0tb9iG6unq2RqdNVFBlvIriYqpjbrroIVi+quetgTo0eWtaqu6jnrRl9XDhwI4VCZx7ARkK8igntylV/vp4G8QFnEOHl6Zk8PYJy8cHI/PxdpoLMwjsUisMo8oDr+TOHzNKV7roTa60CHxQ60eDN+OMg1I5agKwbvUEuLAmYV4QE342sxeytPwaCe1GwaL9okxdV2K5Az/TTkzIN2r4F03Dl7mPY0zM65zR1qY97h2a1vwSC34Sn5uJHvUx3SM9gfa7vJ5yhqbUotqUvdm1KuOhwKwgW+cZPtQ7sM7fW6Q38TIFc9DgaA+XGsQ2XzFFC5WI2q0KwqZzPHPA36VJ+okFAIDXMQuTkKTS/K1+a2YEeasUuB7NwKdvOMNdilj3ej7/SUwh4CcyoFIZkcsjTCHpPOw2yKvLtkkWBaCre7XwxXB9n8uJuXxs8I+xxkD4qC4As4CNvnAU8T7x08FOGe7KcCjIr2xh8Mdy+1eOOjWOgQi0dFOCerevKi8uLDnT3TLfkQ1QCf4TyOt1UJCHZF02Yp0CkuoWCnuL91H0so2H0siRi0W1tZn9UAnNHnN3lxZ3g4U5lnNWfC7X7uULc+Oe/oWRb/sjZ29K6NeeAJ3wDv2rjBuzZ2jEh2xto4MBU7YjAvdYRaUOYHGKKpP+wm2MW4pwma8XMn0Ah2blV0+S8OzZy/GZlkS8Gc5Wd3J2DE0p0Jtnw8FuHlsT/+lsfQm0w6eYpumBzimSWzpNzYa52Uy5c87wEbOWs8cbO4TX9uFPj/1op6EJE9sSrd5p6ZpWo8JtTJsYsVoF+Q5/+4OIc531B4hyzCkS7FzjXlCcpRF336eyn8qZDLolil/JOnYNYF+MZaidk6jRnA8wpRTJ7adKZ75c4FHqK/C+FlmxBP8Do/Z2kg0xhinLziBWl5CY/jdJY0TzElDkOuSqPVHxZUHjgAmsX9z6A8+NSJxGRPhyDNaks6Dqbu/xUP297c9x16kNWObD94DORAg2lQarkFkPEi2Njvt2eRc9N7p8BjzUZTH+ByYgx+BGF/uytC34M4OYc6IaTW5tRBdWP4X5zdb4ET5tG61r8RnCKVFu/ixhn09FyqCj4xWDByb4KsPlVqsOTPyZg641eZaXX+IEXh1aje/H4fiL4Dm0H7eVNB2U2poOmHDaGG96zUx8zYaYzF3AwL5c7TEa7JA/qw170pSj+aDYEfv8bsjQUtQjvADkvfMC1/ztxph4Jl7k8tPwTVFhzAurRgf5ZXIYKO278BSNf/qQCzNOYeWyPFjkMpyGlpFAbQMIWjTR/Ph3vT1LfVYzfC9KbVsPe6isX8ObrSoXPJh/vYZ4+G2dTsja3OU/ejlXXdOpe24GFJUzNvVELFssGuDi8GJ6z/xtqyO6NtMc7YZL9/kycJky8mmQOXHJOcnKx9sb+774LeveH1yyDStu859iZ6DviexIj3weA5X8AUdu+HHutcsR542tmbXe1uSLmVx4MK7KoBld8eXxOlnevWIPtxqPZOnu3mDMY67gwph2oDuddwfwPM5w2tsVh/cS3Dob9Fh61xLMldWZ/7TpasgJk77wBru4dFrwbPhnfVgIRM4VBvDYJp2freGuIC7bnppm5mjaGw8kQtp49AaA2kzyMzum51X7Oyf1dBhVYzQc0+JcBYHV6FNhPY+CJ1I7qy+YdAffP9Esxp3b0QTTaFMWQd0gwlU/bABGo6eSGbvOcBTQeDuVJOh9GxJgF8ZGqHwbeSLX1X7kRlZd9AyPgF1AqMgoHUoX4L1Sw1gKhRQCYZ9g1yYU7n5DZgSWpiL7VgbfRX2xRmZRuYD/bKabqrqtPABaO0a9wyapPcFXHOfZdYY7I5eXzKnsisO6Wsun/y/u5zZnrWz+QQi4rdUugBVkO7gSmvMIBEen+phKF0AavxpFtYIj9Vgp1ZhmlXMRSbExHsitQOQl4RliezIsblhC7wy5filXoYjoNVs7iPPPXb4xh9191BmLmTMF5TRnxBKZyzH5p150nC5tCKBSlGCfLgOeg9xnH6nd3idoF/D1Bsm/9mms7HRzEWXJi19qbOyQXNYXVH6xWNR7BrIcoD1tVi7h9RcdHVnjpEq6x2qo4Exq5ha+97cmKRc1idQ93RWr+gz0UJkOPuCsyE3Ne4qow5Cfa4kjxF5p7fSoGWO6gOYt/CjM6I2e/JCU0DmEzWOgddamP2QZ6RTXMH1UXFU9JUHW2kUa51rDkvdmp1tjZH51StWy+519ucCTF+C9wJZmdkGILQcBBYgJDDgcjg9DhbHFbKUEecO9Nsh4rnjLZb4pJyQrnCpTujHY5kXpmIi/F43ms/pREr6nFO7IdlvJ0TofYMiLgv6Sh2hPq6Cn3x1BGdKme9fNhBSHfuL546LEVLnROTq7cqzV18Feqzv55ORQvrubU+sRE0sKRKlzH4uQHNQHN63uYyg9EAWnyuD5ZIow6GxMSG7r2EeeTDU/C7LXaTbgtvWz1eMXthQuY5yZzyxlXxKGjQzcLp6sz5zfIi+KemrPMLUJRbc7k+UBhyf12k25a5BxgX8DYAvYP/SIeNf5OOAFffRb6rz0J1unnmCyRsvQ37ip5UKlRywhs6VYTzIODmkVelqHTQH0o3LTT715cw+VpCmNTYt5pOz/toMdi/hogJ3F6fOWEElSi93MriW1FvnfKsEjpEye+j2AyIEt6KW/NnFntCV6lBlPy+HlFsgyy1L4Ac7+zFen7tTBmbV5uY6dUm3vAAxj2hTbzBLJ4EFMsDtrwEfnJb76tvGkoj5pcdGK/fKZV+T9iKmVLmKGbsLT+6246DeL8Gy0IUnQLL4uKPMzS+8o9ttYnvsx7yEV9581foQmeiG2sCyZTuZTSB5PGGNFVuKzecO9wPM85tRGgMFibfZQPRx9PmLwmWsO26aDtr1qs8trsF9eXy77ATebziZeI6HparTROSbCjTeOJmh/0uL9CltJIf7O3TNWdLUfo9zt6e9Jx7qn4LWz593stHPLQq1HMVWIOCrJAPJsEjKiUB44ei6KuYqQDxvFk0/jaT0FIh33nIzgsbKAjxkeC8S///K1iQcxufG6UBPj+gW2Epnot2n39CAcz90/SuKN33GeDK2x6RnIlpSZAX1efP+fupBWrErwEsy664q2Sgig+gJDn9N4O9hh3uqgBqDzg3yl3XVhBXRDi0izWUcvD3zZLdpCju/xaE8VvUwxrsKqoApfPYcCZt9mRS2VGQB8WvBZ+uCiIW5aHgpdhxyMaNxmsjYAiU8YcKcKvmcdRVFgooAeq689ZZ8NuM+RjRGjApSmW1Yx9aF/3vzF1qTj3TJkqj6VupoEGv+dBuei3gPqEz6GOA4Je4ST/sBazAPE5zBs9H6ME7JCM37mfyCojV+StAlgn92HkRy9HO0/O5dX8WukqpU9wYsK/PBT0jz+H790yOMrfyOd4p7dc7dAo5AdSK4v2hqCE/SyHojyRdnbmLdnYZwDleITn9KUDqEIaKe8DxMSEEJ1DRjzNB1RpTwcnHYc2IGtM7MAXc134kz3dqfPAWk4ei8kgII1GxGEAFmCWi8gqAStynqvAc7xv4BCK7YwUWO8eXUJTLTUTb5z+EKZnw0rsA3ASU7us8pOhO/E5jZZOnoBdIHiXKM68QSW76pWI2/vw6FK6fuT/32Se/8p7UW8WlaMZ9w5/j7yI05TW0NQDMLZp9VBhgOkwCQV9FUYEXILhLJOi8Laiuiso3eC7lXsa2S0Jp+BKS9uBsavg1TmBhqHEGGn77TWHsLR3OjrNTNc5CJzhu9yGFOPbHIxfyuw1qerosRNLTlfqiF1e4N3ulFIRKyitNOColgWepK32Y8itDPOi3c3u01PW6KgWUWvLaKoDebnOwc7LaO5k8HQpv3EhouYn0OBw3cgOB+pzuAepTJTCLyqyLTdafGzwAn7CCpyZPcdPYaNi6KP28qQCEvsGTSrKT7c13IBiTIB3DShZHd3a0S+MAhIO1bqyrCdHTcSf9JMEQbu4LpO+5BFsnYxAvwki5b+tturqPAcenc7plk0xpHOXcWPDrjWt2dLD5xhNoVvwBuyHsueOYfT9AGbWNKYLZ1/FpoNKY+3y3j5QHB6xw94+R4pUVCUShMGRSGPQm+ag0xsoWXwso0EcndFwFq8oKXU88RwUVCgRPZBbDrizMTfGHs5ZiVtN9wEXBFjgQa8iDpmDaIhnc/WyfiWk3gt4lP0DF9afbtut6POSVi3e0fHlH0rWe+7UR49HQ15/D9k+evcbG2mCu3P80TLcg73HNyKZxl+GcYSUuWJV7JU2xsPw44H1hMuuEIMvhDUzX9fW8PfpDc4gl+pvc70BfVFb0gPr27cKLUEdj+gNs2BloYw8H8JCC7oXpk8+RDANA3I+OkTTA48atTlbSqi4yvXB3pT1vgNU4gaplOXY/k9K8BBM+WlGfA/yM3cIRN1AFXeXMZOW0D98mZ9jVOKLaTgMrJaNCkfGVagi0uLyAjX+MQaPHYvr7Ecj1smlhssUOo2tdr0zeZqNzTwYXGsuI0po8wu/1K5pkImBXLDt5AePxYwA7/hXGCAiCGO2RjwdtbtEKoca96UV6HqGYNiTie2ipOy/2RA/72MYaaW0NpJ3MFYDDjJbHc+i5CbOOnitLYsdZWJ7HFhETWSfmtO+s8mwH6Jl4bSqo40Q9+AofMEQv4jiPTFIO9ezhZaEOT2fa2M3uPG9gQ6Fj/yAtFpIfR5A+SxWPIv/ilw8j09urppMrm28Ks+g5IMJKnOC8iORM28W5kqJmn9UzeTAHmnFWXea8aMaZdkfPFc4+dDxi+ep08ILzNrdteVMmixxdaDN49AF9gQK2wS64Qxs/5HgW1iR8iqCvQOm0LVB3A+ygQOExndHCcCgfgVwwrnIr3v9FcNXFRaMwl285MPcrFqOh/oDa4AO6A2XNgOKb2bYcW+Y2d/8SaKH2vXsWFoW9U7YvNz94F6vk7lJsAkpE6MeV2SwWmgcp4zUq8h63yO7Y9yrAgxcsprk5N62gkbXISlUxanyjim4MvXlDI4DeuFgGCkox4nMPqpKw0Rf6QQpmVgZXoNCPMf3u06+5JE+PslbbiNcHVmkUpetuqmolYNvrmkjCn3xDwqHlm4qYwOdI4EELi3YlNUazuoOk0DLdBKh2ms12JyVhuxVrXVRTV6EnQlIaWbnNYdRAUxD3KUfRo1vokTxdHYrDxV51lODnathso8Md7Y42j+N/DvxbMG4BjEhntE4zM3dtvSygBqxKlU+8w7VCbdG82BgrccvT3TwAn96kyryuwiZ1UTaJEFS4skuOMHUS1CZwjbei3g5WAstne+UVvBC/ByuQXEqPr52j2tEYTQTLFs7iRtl3GFfbmT9QoPjGzoA1f4XEPLMFGkmlNpBGx+pcRQXXn1lNnwDe48p6EcOfQX3s9pBJJK2Sm1SoGBLRdZaamU5f4blk9EpYmfxXn8mr41gdOpef2VJRNeLI1wCznfYUKDK3OSt5CjT98hHI2+s8T9zEyakQcdWeQoi2uiwKpUDQrVlaXmMI5BMPu+lUD+Dpjzh5wApTNc5WNcDV31QNQsm0aCOk2V4Lren9qdVACmiS8ii3XEoHQ9v148bfsv0/SNGWm1SbQ/pJ0WVEaV6PHrHIwViIpbZ0TPDrMEaKfzUswtIwjS5zM2J/anUgD51ctWB1Y0qP69veVj3AGRtWPSDY1d8Za0tyLEjv4bDaLTCq0cAkkUT3C+HgYNFZaNooIPg1bYSpHwCXs7+vs7oT/aSYRsfOu8ejQO0EaOjnqPdOrRnsSHIkLXAkodaz4xw8MwydJNJRDDVac0xnbUkLYvIfWeS7hxXruYeljz7S8o4skhfEWfLTjPeabHb2wA4hWi4QysnA3hshS24Ohip/flhhTMROV1FBdCMpf+NGJyAVzdROV8BfdHy7OXgLSs6+yx30k+jdtec6YUxLu2ECHh2NSuvRWf4ClEHRuGV7Rx5krIig3cWcota0LOvf4ZXsDrW5E+OcoT0sBW15F7iZtCXJ72+7KV+5/6aUJb9sT5eXee2I/D2I0NWUFcnQQaru45lZ1X016BaFaYTON9Y5QZBgsG61alD8S6yDyikvmz5TyEt5iIe87JDcsz3bQbjRoWNV55Hzd0WQJ1lWFMYKlntw/BCQfPsXEyDBbFCvf87AUmfuA8MllcffpSkuy07uLuWvK3dgKnJX4/xZiIUmi9CSiF05ibx7G2tfC54JBhyH6tglGyr4TXprnXirm5QHPoZsuxAKubrpQivI40pfoh91uHJ//948sDBHK6uFAEVClZZbZDdkhIvrkuWTGEjtYpCXxh46D9zGQiRcMEyDqnDnwrcDbcnCvDChdg0CeVf9T1sM9wchNWP0DDqv5vByOcvLe6cSdZ3yUZHKuh0RwONPti38KKGJy5/olcA9AN86S0GvSnRw+om6QKFmkP2nF2KNOfMOJlML0cosc+VFiNDv663JG/e/8EMk4yNRpz1ottrKM7nB6ddBe3n/udkIRUJnugGrXjqh8spr9CTNunsTQ8l7vIG/38cnd5pLnP8J3bvuBGPAnJHHREutXsiT1P8zbjyFYWzeBB2GvSTT6T969hHY1MRT8rPb6RVQbjivnLppzhQPwToauG44Qmegq4Ttg9FujeHFqHPZkKTBGN/Sn3bjSvcBPoOOAy/6WxUuHsD2IDVqyvHU9KCoVxsJ5ahwGBaDijt2Y7x8fwNFuQJF+hZDa32gPEe22P0EfWPcu216WzdvRzdR7ejq3WC1EaxBxbwEnUVl9uwGP94hnqM+w52hzjZkMSUlpdFcjtcHs385COWminUYVoCcrRhKP971yv42BDOBCp5/dC7dtqkO8M6UL2096xEQy6cRpqB1NijmQ4PI3AAMZv2OfhAwmpFAC4T6mwo5O/VjFijyTjbEBCnbYVwkF//bRzG6bnqU3+ZFHmcnKPe3foWynnUNXUsbhGmeQQ+CXI7k9baXlYMk1fOh7WmSwjiRcVN4Q39IM8+Veno4oX8bvSe27bkj8iyV2hZje1ON+vU80NhLn3068eRUb6a7Kt28g5b9bTBA+yxNEunqSkc+52wqrH89Cy6IpNzvFHPf1dgJkcj3Alww3cScDezGNbAZvbzTBsPGLvxTU3YXeoM/b0eRBJqQt3kFyKQu46tb+rW6avKB4DwO4v207ANjSc3+XroOYGg2ad2WWcqR/iNUFvD7SG4S1H5/ie2/uJro8h4xnX81W5q3mjUKc1idic7L1P3O8tDz/hys2eciMH/Sy+PzMq8e2Wpj+M/wHZXA0hj0lCu8JXgspJ9V3RX0XBXMe2EiX5z3JQqOQM07i1ge1LaXWsw6YatU4iMw6ZzFpS0D2XaUFOUvTEbuyANjeMNyZyCIzg0221IosFbubRbjgh5yAH05TUUvgn5tf3CjoIqlElizz2Q0AHtFlMuGmlyf73IUo/bY+M13HXmwPp+YuEmN014CZv95M4wlUYVrV+EvxrNKYBrOXQg2IyrH+bDpfihCDYLoyLQey0CCG09nQnPJaWOrEfCkgO4DHSL3G7W4PrFOpnrXySUFrpNL8tZJ3rLkOqnLexgg3zr9XWnvOm3nOr03Ta3TPf+6TkfQWMvmqvZfkwBeAdDRKwAmawHwH03Rv0i94H8r9Sp4pV4NTsKFXql3wSv1hqN98aBFmI7461yspacrKitP+TNINwD50/S/IH92kjgDKH/Ga4L9Rf6AsKLw9mxNbMifRK/86akHBfKnMwfqxpPy55weUMifMexQRZXwxJLmjGxis+c96OCiv4sC+IwYF7PA86QD4rB0+qk3H6KiwnX+KB39OH8Pj9vQY3cirVNbCvIOiki2/Ce5tzzG5KAXk+hwd6SbFy9uJXPr8vZa0MGX110yOoMZLJSl8k5ca0yankMwSULnPASj8ugyDCagzKavQwllbXqkJAWaR0ouy5OSl7ljHDJ6BibpjYVg/jN7wiEbeap3jd7MRwf0eVIFgx3c3+ZMiUPvNtHk2MveKYBP5ajjjRaN2M92AwdGwGb5+p0PIBK6Txwdw4sqV3klKoCC7xqdSIrybYJbfIcigC4FD9ZQCarLuywvgjOD2kB1drfta0U12RNH29Jj7KDWe3sgSMo0XsyjBU78olEJrBLzvEQRPg1UclIra4xq18IqIENNSoyrba3V89nUBQVAhEDMbvHVnj68xVQ2k1e4U+isOpX3nut8hZJ8LUu+uQtxpIzcdBah5tup/9bpR0nj5k4/NHyAc/ys6AGMk1eQKi+Yzin9lxa15CXanB0tWOc7PNmuh1DdKmi2dhndtsyB7AIu6xx/wmiozgmMrvJZHojBoi9D485otok3xcuBimrrWkPGynuUCswpb62IZ+mqqIzOKu6UsfnasUIZftzWWG/vy3l7/25e7/k2VwG9P+ftfYV8cQX1voy39+96ez8or/fcOdJYhTdweCH9rz29r1xA7zd6e9/UkQcZ+9fer/T2vrG3968rFncl2GoG5PQ31QiIg1IQk5zsl4TJlwTD0x6XZI/T5+J8Z+8pdeYTzW2mNO7YVOFO8KkN+/OHbEtAqeTdvPm4//WrCl7R2RHbQ2d6AhxQ2Zmldr4iDh2L6o7qTGjiKW+ooc4OVL52EK3SVvVTSudXGKr8Kmu+kEoghizjsG4x/b0y8/gJrT89q/Wnp7YeKz0sFhHnzlQUjIGq745NdsaSeNKY9RVPSFYN2SFzZ/ZcJXOXV5/EF2gQwaMTmekMcCiod/6lc/EeSM6ymZihlUZNlbnPvrGSx4M8HL7IbdDgZvUhQL7uI0WFymlQrI11y4da+LGCXrjNoMCU4EHVnZZYHMvuLqGq2S+N72fw0NPJ84bgi4G6QbUvrRCwxuWEwjZMchKjv3phRsy1/+iYm24uyGszPDjZmWmzxzljC3YYfUL25/NHjY1L94tJVwRzZobZ3dHpcXa1ekQyV6os+CXDSG98ani6xR3dxObZCnLn92r1ZDflbzG8R8EOsHZnQnrBKMbBQE6OifFLTlc/zlh3rC0uyd3GbW9zot8gp8kdaQ4+KY1t9Koqmv0HXeMiu0v1nwoJH77qZfpuCQHEbvTFWQTHMC7lWmCN6FQOI9bxl7cVfBrDe34rY6FQJZeNZ05uavNqiz9dzDIOzXbo7ycBJ/2hY55MWfTrt1OpS+EdvhIVMNJ7tvF5j51+GVb1AzXvGo9j92xbx1yxADWPe6t7j9Udtnmre0/jLt3UIC2RJaBwrKjL0ELadCu3g91qtmxvqxXAj436ozY+NlS5I42YmsG2OjR/X9t1CrZf02r/U76i3NQaNuH1vyaJ/SvflsbGlRPxJY3iL02SuXd3zsLg/8I3NOtTSc4DIGd1RMoTDKr0x0BlUikO9eOJeCJTXkp1Bp+s2lMJU8xP4oSvk6G50tgRMNFWFzOrWrqPlM0fvGuDMKAP4hOpBlNzJVKT7VjX7THp9uR0Oir25dnMrVUO0L8SHf5vzL6LQbyKGfFb4z+A19H3l9DrJw42497ExgSN+Lk33O6IccQ5HK7YJEcy/mGitkO6aBS3l5nGMtMUujVc+HY2JvwaKI+XeDX7Fq/R39j8BSrtXRgq0ewub9q4xVtvh/CjwgRxE8arH3SvVt7W2uX6BuWQcrm+bCoRgeAOoQ5ry1I/L067vswPu5j3aQ3kdSYwTl7cOxCmzppJwh1qoR9zrM3dn7pROu+HZ/EM6eYWLJ133qiMiViyG1D8CFbCnQF8pYSaUxbvkF4p+ZkjDzJW+PM+KXOKEOh/KA1lIfjqCtYIW7NYm3PcRbY1tzr7x6A5XgiYQ9fkmbwgMIz+yW94fZaf1zcHeIkg3Nnfz50CwRjjTKFzaCyxTYhLhtjJCaWjtDPB4UiKcfDp0GS+vZvo5Nu7Q55ex/tX9KcN/Qbr1hk+81GoK9a6U+8e4axJpqKawlOFsrw3XGHNVQ+w8xBDxUUzNdp/DONglV/kjZLfXygF/Zp2Hx3RbiVNQSkeNV8IgPlSnHcCL3auhQHj1b7TLBrI0Mnb8x2EXR2M3Uyn1nXMWRPDyNCFAAzodUqFW3wI6/dbnbg1MXORhhBsseNULLQbPi3FnMKILAkZx3u9RlXe56qOcTbsbRCq0dGqCBDnTLQ82YEr7EBp1Qt2foynU8lM/StBImIsf0Ig2ItABrJ9e1yjLypntdNdEtWUAlytyCAeMWVN091HzmOaJCi9m2QKYY1jSbp7mpTcduD+nXqwIKw4tOd7fEOiGK9NiTdb7LYEp2PcXaE5+GdxZibBDnDjn19MDEKOJCycmHehSXGYc/N4rl7tlQlggmOw6yDkQAR6hYg+woBAo3Du8Mp4Z6zDwScPPd8TwDnRynU0mmaQnWZQCq2iZJhf6kwjmWaZikqhbZZMG4nXNjzxh1goisUPRahV2FNPlK6M8eGuUPpLxvKhhhAeQj2kPA8J2AHicVM9kJL9Pq1oFbrzAOuThXL/jg9YVjV2x4fXd+88aM7C5XS5PHDNE3rIbTEUdqiCVjfbY0FWdl4XRgMl8zd1n0dWIQE/68JArja0crSHEG8IhQRM0oWRGytqYMXfCE56PokiC6te1ba618fZHM5EZ7Q7FOLZcPPFrTZJ74OyBq835XbBwmPUh/qdW4iXpfZCQDxK95y3y+wXzntcqB7y9Uc3VwM3t1TUOAG0olf1sNmepIfdoEXn/tKMBRcpvwtPZara3EArm3qHjSaweX9um+aO4iOfoGV/7nUs6BKlQ7L/KnVrkJvYUk4gmOjZSvWACayHmwexjYj8FLb8NtsDYrJRN42D9t14+K9ubBkzxT0UjGGafU/1oJXmNCJfWMXqvgoLHZl9IsD6Zt7sEPTl9wkdp5EXRmYjul3w7gNVPkFfBz6/lQdaesFkKzsUTZDADoEBF3TmXT5eNRFGe7ryxjbOGwggj7q3a+TR3ljlM0EcyrFlrB2WZWlE866nB0B+O8EOs0YegJsBfBTNaGBiQbq3X2jFykhFrjdEHk1NZKMdVPPWvEdiidSCzvfyIarwVoOjsUWZ6Xldzxyv2UYhChINZHv99JgrHBQfaKK6h3VS2E4k0acobDM0xyhENduogcqGJa4QxTC20YgqvsRIu9RwezhGv3rbnwu3B9vhKmQOdiaY/s42OcLPOgVs0h7xxtUpcOPWBYNYDPHHLFiJxUK8lAWc52a1C5fybF8+Nfv7PruUD3j99+JFLJ+3+bab4Xejocx1hU2jfcAd1LJlIY8DeIe0Ane6ReSi+tac0DDWZGKd7Vh7DtvZHM7UBnwNpeFslmnP0pmsx21lnftZ+xjeR2pAhwNaLg+2jyYefFnnbN9fvEasM1TKzts32pRuSQE8CpLzW97u/JbfBKIzET+2fSO1v2NUQ1WENx3gA5UyWlXi0OkAowlGqXSmfEhv1Zq8Hcz6mc6XM9E1K3ff/bgDrAA+w10Jlnzx0sX9fWe1nAi7n/r1xORFh+fQjcwb760mItg12bne9p/UBDuljaXAFHvBTThRwK/AFF5QK6BrzjbONpYCU9IL7jPqD3YlmOoFuBKSbFEBUP9MsKz45wLoVpKcnG4350/PH06HjelOhGxP9PPuWsSaogPsTnV18xFYxk7fawXUpzdOpvB1t2SGkr2fBcR5y4Y/0ULeR5zdsz2Swu2RaDsswVDuj7higVqyui54ZUJtnv4eITjKzzrhOW3yP3n8+NSPqq8fNwycUDChTSQ5E21xzpQkP3doDAKhfw6k43e9M9RGQsEepjKKD5R1pvi5YpNtOYk5sa5EV6zFjjod6Y4kKb+aJKDMjJ8kgKw72hFjdiY4+RD8P3tcwwM8D2r8Jc5bNlw/9FofBtsSvsCw/UN6yOfyrx3I1fMRGn6al+BgZ+Tur0tQh07sfIby1Bb6PT+azjvb0ymVuQrn0FI0Kl1E6AFWfvFaDZS5DKVQXOCBXnEIZXH1NYR4L1105vuD34ZBnnzL0zsArqMl56t0T42SXnpyPlfry9A8cmn75jg/Yo28u+3H9W/E58SBN5gr86nMU1sq8B7UG98wFqPmywetJJ/z9A3gylWROPJkSwxuAHB9ZJ4bMV8IEFcHErMbWzR6HhyJGZOkfolHXOe+HL12jZJ80GHeL6ibbqC557CG+rqg4IgqfP2CXrI+I+hlWYV+OlXanFOYDfIkqWYVerlnr2r0dGVET6NM9BQ5iB6vUHOvXGHWhS/8dL7BS0MacwUqLWGZBE/njNKo1qjAhwGsG+ju3Bmhp7HQPZr9OnHgO0ynNoURs8/UaE5X+IKwvLwgLmKpybmf4CF7Li1ZOW8/B2VG3vCAHBwy6+Mx5JC2VThyNEsvJ+EG6YHUbMCBBHowkHPHr0JoFqzDR+pZed7B0Ly36qD1MWc+d+QqnR3kw0+m8+8TLCHg08+fHIHduLnWS34qzL9l0FsnCcughHBp9PjqpEWFmbSIYDE/68LmbXuUT2NbeMUpe08/ncT6w2EGhroG2z4PDCgjgoqJMP5xmi5GOeOWb3nfkaY40zZzlPl982+WapZxlkN+EX7Z1u/9/f1n+n/i/4X/3QB0ogafCaio3ighUJ/eOJnKR1qOM3Tc+1lAnLdseKH8+5xLLgj1vCv3AvK/79re+75r9D9+37W1931Xs5Xvu/78P77vGmQkY3j4zlkQJaIkWwXN4YP9+dSYXlRG46nKxStkmdCrnNBbrIXyuZDLb/hGTuKWZezMLrruV+A7bAcW3kZzz9A3o/8CdIG3ztzcxxI9+JQ/3waQ7nhqD+d7fS/l1XIQTZfeWIlPPkOS0ZnnfPeHn0ZnmrwNw9rojjnUZFq851gyiu7EUbzzF8hd9dwEqMqBE0fz0GApQpsXszOPPdq78nmbOJ7DEGjPd8axFxGFeJhwrTowo9Xrm8K/ZsC/cuLLP9JRm4ZP+Pt9eSWqoRS1Pr/P7fdmo6l5WG7A+l0OpbXqqSJQgn7kTRInJeNrGOfsE+/ywTV6th+m0Z+9l16T5+fBxHbRtLywFIhkfI4pfPHjKliWJkASXX4aC1c6eflaA+S7Qvf/tPM/gRIH6SNwklbxFt7muM7LhT9+sxn9/akJmKwU/xBBD57xu3i/MhwLQ+2tVXlPyMobSMorS/AEL+BjSC8DFo+oQU32eM4t5Iwdz4fbIJmiZsGgzOWlaxGwpw9m4+Wq9AkeAQ351m+YlyXG0gm0tGuOtdA/ffTqH792hRqLfTGRSQeA4fKq4IbKA7kBzJNxeuE/BpcqD2KWofqFcRTj37OYTyH7lno0I/NHDELgB2+B6Dy9DRo0Ur2Rhk6l0L9xNHj2Um8spSkTN6EK0vPIyDe5I0S9WKvEVLiV/q0BdeW8uM3eOE9mFiPvoxJPdSkT+ecTuHN7jm9oslmNQBBEmEYqkA/1Zv74ESnZbu0g3u6a733GobIG6kqhfsvhySuF+q6hN04/171Xv/5QhZPzHk3Htp9+xovAr2Hum7l7aJ4Mce2ftI8MkTXV+k+ffuDZ2QNuCrG8zILpizpB++wm9IJmS3LzwIHmQgXZMf/0z6MUdAbEfnxEgaOEZZ7E1EBLIsYp6aRBr7yEx3E6Sy9PMdSoNSzFdEpz+hPX/knDUgyr4v7EtV6tq9A/dUb4x9Qu9E9780+pw17/M4r/0xFkjepgBTb6k0csvKLOTaKqvElalY+xh40+jGWgWX0wXaO1fEqm0bpakJrT6iGW+stvUS7kTN7/pyMWz5aLPg2hN8zfH7O05JNZ29/mqevcOB8VJOCbaPkPUw4d9xba828OU3TXHN5uHWHeRQQ9sf60PkXwGy91Dn9Jyt1bq+pIVPpuHvKHQx0aM2/zf0J8ggfxqtMPov8j+br2dnNfBqMJorxl8h0CqTK8zeJB/FUv4nW8iMupL71D8G4T7mueBZJjVr7NiJ18geLEJCwM1bGYBVAQmf7VDelFKw+0uur56RBmuU75jBnfJpjIWnPq8Y+FuXtjcX20YBldRbsrYP33DZrcXzMbOeLLZBatiBX10Atcl82P+A7BLXoF0dm4UKneEcBCDzm+eELPaxbmDDrX+BZF31yJMKwSLYVcsTn451conwj+k6h+LL61NPeKdK+o9m5/mLzbHzlqjvSFvh6/qAFmzuVzmAXE46MKzwPw1ZAl9P1FKtS0e8O5lX8vEYJQ8NrTvUfrsBwtgzJwwPgAgJv423mNJIun9DvoeJarbkYFHbkCdaYCL2CH7PsWygIvXYfHr6XKlLkV0HSpqMdHyUTlRPIlXNP3WFX20rOuMl/CPcOLYo1Dmbtlc2gGIv7KZaKNmnrxqfOP+iaHF/p/AH+bdUQAAHicY2BgYGQAgpOd+YYg+pzRxtkwGgBDGQaMAAB4nGNgZGBg4ANiCQYQYGJgBMIwIGYB8xgAB/wAiQAAAHicY2BmYmKcwMDKwMHow5jGwMDgDqW/MkgytDAwMDGwMjPAgQDDgwUwdkCaawqDg7L0BwbGB/8fMOgxMTAoNDAwMMIVKwAhIwAxtwwgAAAAeJzlkc8uQ0EUxr/LJSnSXk3rT6sZentnGlVR3fgXKsECodpUxMqiC8LzSCQWnsE72NoJc9PFeQNCENykpnNvquIRfCdnTr45ye9MzgDohp9jMNQJ41o5Q3vTOFe1ghJ6EAXspH1k1+0TRzoej/BBPsRTnPFFfsavREhYIi5GRELUc7FclkyK0jAlKUWM0pSjIs3RAq1SmapUo8MnNJutaQHzWDG/eLjNnOenmhnuYDoExYxTQjMnKEN5zVyhdc088JkNr3HfuAXcfIvvLslHN+zGAHkpL6SpalEW5OzDzV2p1Weeik/2MT7K3tmb8q9BvLBnwDL8nWht6txu+42gruFHy7+cf/PXFTADCxH0IYRpTGFSbbwfA9hDGTvYRReqqMHEPmxkkIYDDoGsml7BlvqhXnQ863/qGwRdZdYAAAB4nGNgZGBgAOKcbi+zeH6brwzcTAwgcM5o42wE/f8BkwNYnIMBTAEAFkcJ63icY2BkYGBi+P+AQY/JgYHhHwOQBIqgAFsAXiED0QAAeJxjYoAAJkqwAxJmYHBAkpPBot4Bj1kgOQUkvgKaPDqfFKzApABhI4EEIAS6mkGBgQAAAAYaA9UAAFAAAFcAAHicjY4xCsIwFIb/aluQOotjBkEQUppKoXQtODp6g1Lq0ECVbl7DG3gMD+AxPIB38E98g4ODCcn73suXlwCY44oAbgRIsBSecG6Ep1jjIhzSuQtH5KdwjCSIaAbhjJWFv+V4whsr4Sn20MIhnZtwRH4Ix+QXalj0GNFgwIm7xpERte3HZjg1+sjEVxzsvHz2cUBLXSFHioyx4vrd7HNmUDAvuXL6Blu2s/15Z4e2UXmaqUp9PcrMFLrUeWYo/vPJg5x23nXvuX/hQKGzvTJp9lefN9BfQHsAAHicY2BmwAsAAH0ABA==) format('woff'),url(data:application/x-font-ttf;charset=utf-8;base64,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aTI1RkMHdW5pMjVGQgd1bmlFMDE1B3VuaUUwMTYHdW5pRTAxNwd1bmlFMDFBB3VuaUUwMUIHdW5pRTAxRAd1bmkyMzFCB3VuaUUwMjEHdW5pRTAyMgd1bmlFMDI0B3VuaUUwMjUHdW5pRTAyNgd1bmlFMDI3B3VuaUUwMjgHdW5pRTAyOQd1bmlFMDJBB3VuaUUwMkIHdW5pRTAyRgd1bmlFMDJEB3VuaUUwMkUHdW5pRTAzMAd1bmlFMDMxB3VuaUUwMzIHdW5pRTAzMwd1bmkyNzY0DGludnNtaWxlZmFjZQd1bmkyNjFECXNtaWxlZmFjZQd1bmlFMDM4B3VuaTI2MzkHdW5pRTAzQQd1bmkyMzY5B3VuaTIzNjgHdW5pMjM2NAd1bmkyNjFGB3VuaUUwM0YHdW5pRTA0MAd1bmlFMDQxB3VuaTIzNjAHdW5pRTAxRgd1bmkyNkEwB3VuaUUwNDUHdW5pRTA0Ngd1bmlFMDQ3B3VuaUUwNDgHdW5pRTA0OQd1bmlFMDRBB3VuaUUwNEIHdW5pRTA0Qwd1bmlFMDREB3VuaUUwNEUHdW5pRTA0Rgd1bmlFMDUwB3VuaUUwNTMHdW5pRTA1NQd1bmkyNzE1B3VuaTI3MTgHdW5pRTA1OAd1bmlFMDVBB3VuaTI3MUEHdW5pMjYxMQd1bmkyQjI3B3VuaTJCMjgHdW5pMkIyNgd1bmkyQjI1B3VuaUUwNTkHdW5pRTAwMQd1bmkyQjE0B3VuaUUwMDIHdW5pRTAwNAAAAAH//wACAAEAAAAOAAAAGAAAAAAAAgABAAMAWAABAAQAAAACAAAAAAABAAAAAMmJbzEAAAAAzjKxmwAAAADOMrGb) format('truetype');font-weight:400;font-style:normal}[data-icon]:before{font-family:Converse-js;content:attr(data-icon);speak:none;font-weight:400;font-variant:normal;text-transform:none;line-height:1;-webkit-font-smoothing:antialiased}#conversejs .icon-address-book,#conversejs .icon-angry,#conversejs .icon-attachment,#conversejs .icon-away,#conversejs .icon-blocked,#conversejs .icon-bold,#conversejs .icon-bubbles,#conversejs .icon-bubbles-2,#conversejs .icon-bubbles-3,#conversejs .icon-camera,#conversejs .icon-camera-2,#conversejs .icon-cancel-circle,#conversejs .icon-checkbox-checked,#conversejs .icon-checkbox-partial,#conversejs .icon-checkbox-unchecked,#conversejs .icon-close,#conversejs .icon-cog,#conversejs .icon-cogs,#conversejs .icon-confused,#conversejs .icon-cool,#conversejs .icon-dnd,#conversejs .icon-envelop,#conversejs .icon-evil,#conversejs .icon-eye,#conversejs .icon-eye-blocked,#conversejs .icon-globe,#conversejs .icon-grin,#conversejs .icon-happy,#conversejs .icon-headphones,#conversejs .icon-heart,#conversejs .icon-home,#conversejs .icon-image,#conversejs .icon-info,#conversejs .icon-italic,#conversejs .icon-key,#conversejs .icon-key-2,#conversejs .icon-lock,#conversejs .icon-lock-2,#conversejs .icon-minus,#conversejs .icon-music,#conversejs .icon-new-tab,#conversejs .icon-newspaper,#conversejs .icon-notebook,#conversejs .icon-notification,#conversejs .icon-offline,#conversejs .icon-online,#conversejs .icon-pencil,#conversejs .icon-phone,#conversejs .icon-phone-hang-up,#conversejs .icon-play,#conversejs .icon-plus,#conversejs .icon-pushpin,#conversejs .icon-quotes-left,#conversejs .icon-radio-checked,#conversejs .icon-radio-unchecked,#conversejs .icon-remove,#conversejs .icon-remove-2,#conversejs .icon-room-info,#conversejs .icon-sad,#conversejs .icon-search,#conversejs .icon-shocked,#conversejs .icon-smiley,#conversejs .icon-spell-check,#conversejs .icon-spinner,#conversejs .icon-strikethrough,#conversejs .icon-thumbs-up,#conversejs .icon-tongue,#conversejs .icon-unavailable,#conversejs .icon-underline,#conversejs .icon-unlocked,#conversejs .icon-user,#conversejs .icon-users,#conversejs .icon-volume-decrease,#conversejs .icon-volume-high,#conversejs .icon-volume-increase,#conversejs .icon-volume-low,#conversejs .icon-volume-medium,#conversejs .icon-volume-mute,#conversejs .icon-volume-mute-2,#conversejs .icon-warning,#conversejs .icon-wink,#conversejs .icon-wondering,#conversejs .icon-wrench,#conversejs .icon-xa,#conversejs .icon-youtube,#conversejs .icon-zoom-in,#conversejs .icon-zoom-out{font-family:Converse-js;speak:none;font-style:normal;font-weight:400;font-variant:normal;text-transform:none;line-height:1;-webkit-font-smoothing:antialiased}#conversejs .icon-home:before{content:"\e000"}#conversejs .icon-pencil:before{content:"\270e"}#conversejs .icon-camera:before{content:"\e003"}#conversejs .icon-camera-2:before{content:"\2616"}#conversejs .icon-play:before{content:"\25d9"}#conversejs .icon-music:before{content:"\266b"}#conversejs .icon-headphones:before{content:"\266c"}#conversejs .icon-phone:before{content:"\260f"}#conversejs .icon-phone-hang-up:before{content:"\260e"}#conversejs .icon-address-book:before{content:"\270f"}#conversejs .icon-notebook:before{content:"\2710"}#conversejs .icon-envelop:before{content:"\2709"}#conversejs .icon-pushpin:before{content:"\e012"}#conversejs .icon-online:before{content:"\25fc"}#conversejs .icon-away:before{content:"\25fb"}#conversejs .icon-bubbles:before{content:"\e015"}#conversejs .icon-bubbles-2:before{content:"\e016"}#conversejs .icon-bubbles-3:before{content:"\e017"}#conversejs .icon-user:before{content:"\e01a"}#conversejs .icon-users:before{content:"\e01b"}#conversejs .icon-quotes-left:before{content:"\e01d"}#conversejs .icon-spinner:before{content:"\231b"}#conversejs .icon-search:before{content:"\e021"}#conversejs .icon-cogs:before{content:"\e022"}#conversejs .icon-wrench:before{content:"\e024"}#conversejs .icon-unlocked:before{content:"\e025"}#conversejs .icon-lock:before{content:"\e026"}#conversejs .icon-lock-2:before{content:"\e027"}#conversejs .icon-key:before{content:"\e028"}#conversejs .icon-key-2:before{content:"\e029"}#conversejs .icon-zoom-out:before{content:"\e02a"}#conversejs .icon-zoom-in:before{content:"\e02b"}#conversejs .icon-cog:before{content:"\e02f"}#conversejs .icon-remove:before{content:"\e02d"}#conversejs .icon-remove-2:before{content:"\e02e"}#conversejs .icon-eye:before{content:"\e030"}#conversejs .icon-eye-blocked:before{content:"\e031"}#conversejs .icon-attachment:before{content:"\e032"}#conversejs .icon-globe:before{content:"\e033"}#conversejs .icon-heart:before{content:"\2764"}#conversejs .icon-happy:before{content:"\263b"}#conversejs .icon-thumbs-up:before{content:"\261d"}#conversejs .icon-smiley:before{content:"\263a"}#conversejs .icon-tongue:before{content:"\e038"}#conversejs .icon-sad:before{content:"\2639"}#conversejs .icon-wink:before{content:"\e03a"}#conversejs .icon-wondering:before{content:"\2369"}#conversejs .icon-confused:before{content:"\2368"}#conversejs .icon-shocked:before{content:"\2364"}#conversejs .icon-evil:before{content:"\261f"}#conversejs .icon-angry:before{content:"\e03f"}#conversejs .icon-cool:before{content:"\e040"}#conversejs .icon-grin:before{content:"\e041"}#conversejs .icon-info:before{content:"\2360"}#conversejs .icon-notification:before{content:"\e01f"}#conversejs .icon-warning:before{content:"\26a0"}#conversejs .icon-spell-check:before{content:"\e045"}#conversejs .icon-volume-high:before{content:"\e046"}#conversejs .icon-volume-medium:before{content:"\e047"}#conversejs .icon-volume-low:before{content:"\e048"}#conversejs .icon-volume-mute:before{content:"\e049"}#conversejs .icon-volume-mute-2:before{content:"\e04a"}#conversejs .icon-volume-decrease:before{content:"\e04b"}#conversejs .icon-volume-increase:before{content:"\e04c"}#conversejs .icon-bold:before{content:"\e04d"}#conversejs .icon-underline:before{content:"\e04e"}#conversejs .icon-italic:before{content:"\e04f"}#conversejs .icon-strikethrough:before{content:"\e050"}#conversejs .icon-new-tab:before{content:"\e053"}#conversejs .icon-youtube:before{content:"\e055"}#conversejs .icon-close:before{content:"\2715"}#conversejs .icon-blocked:before{content:"\2718"}#conversejs .icon-cancel-circle:before{content:"\e058"}#conversejs .icon-minus:before{content:"\e05a"}#conversejs .icon-plus:before{content:"\271a"}#conversejs .icon-checkbox-checked:before{content:"\2611"}#conversejs .icon-checkbox-unchecked:before{content:"\2b27"}#conversejs .icon-checkbox-partial:before{content:"\2b28"}#conversejs .icon-radio-checked:before{content:"\2b26"}#conversejs .icon-radio-unchecked:before{content:"\2b25"}#conversejs .icon-room-info:before{content:"\e059"}#conversejs .icon-newspaper:before{content:"\e001"}#conversejs .icon-image:before{content:"\2b14"}#conversejs .icon-offline:before,#conversejs .icon-unavailable:before,#conversejs .icon-xa:before{content:"\e002"}#conversejs .icon-dnd:before{content:"\e004"}#conversejs .no-text-select{-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:-moz-none;-ms-user-select:none;user-select:none}#conversejs{bottom:1px;direction:ltr;height:25px;left:0;position:fixed;right:0;z-index:30;display:block}#conversejs,#conversejs input{color:#4f4f4f}#conversejs a{text-decoration:none}#conversejs ol,#conversejs ul{list-style:none}#conversejs dl,#conversejs ol,#conversejs ul{border:0;font:inherit;margin:0 0 15px;padding:0;vertical-align:baseline}#conversejs .emoticon{font-size:14px}#conversejs .hidden{display:none}#conversejs .locked{padding-right:22px}span.spinner{background:url(images/spinner.gif) no-repeat center;height:22px;padding:0 2px;display:block}span.spinner.centered{text-align:center;padding-top:5em}span.spinner.hor_centered{text-align:center}#conversejs #toggle-controlbox{float:right;font-size:85%;border-top-right-radius:4px;border-top-left-radius:4px;background-color:rgba(83,144,200,100);padding:4px 8px;margin-right:15px;color:#fff}#conversejs #connecting-to-chat{background:url(images/spinner.gif) no-repeat left;padding-left:1.4em}#conversejs .chat-head{color:#fff;margin:0;font-size:100%;border-top-right-radius:4px;border-top-left-radius:4px;padding:3px 0 0 3px}#conversejs .chat-body{background-color:#fff;border-bottom-right-radius:4px;border-bottom-left-radius:4px;border-top:0}#conversejs .chatbox .chat-body,#conversejs .chatroom .chat-body{height:-moz-calc(100% - 38px);height:-o-calc(100% - 38px);height:calc(100% - 38px)}#conversejs .chatroom .chat-area{float:left;width:200px;height:100%}#conversejs .chatroom .participants{float:left;height:100%;background-color:#fff;overflow:auto;border-left:1px solid #AAA;max-width:98px;border-bottom-right-radius:4px}#conversejs .participants ul.participant-list li{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;display:block;font-size:12px;font-weight:700;padding:.5em 0 0 .5em;cursor:default}#conversejs ul.participant-list li.moderator{color:#8f2831}#conversejs .chatroom .participant-list{list-style:none}#conversejs .chat-blink{background-color:#176679;border-right:1px solid #176679;border-left:1px solid #176679}#conversejs .chat-content{position:relative;padding:4px;font-size:13px;color:#4f4f4f;overflow-y:auto;border:0;background-color:#fff;line-height:1.3em;box-sizing:border-box;-moz-box-sizing:border-box;height:-moz-calc(100% - 76px);height:-o-calc(100% - 76px);height:calc(100% - 76px)}#conversejs .chat-error{color:#8f2831}#conversejs .chat-message-me,#conversejs .chat-message-room,#conversejs .chat-message-them{font-weight:700;white-space:nowrap;max-width:100px;text-overflow:ellipsis;overflow:hidden;display:inline-block;float:left;padding-right:3px}#conversejs .chat-message-content{word-wrap:break-word}#conversejs .chat-message-them{color:#8f2831}#conversejs .chat-message-me{color:#436976}#conversejs .chat-message-room{color:#4B7003}#conversejs .chat-date,#conversejs .chat-event,#conversejs .chat-info{color:gray}#conversejs li.chat-info{padding-left:10px}#conversejs .chat-date{display:inline-block;padding-top:10px}#conversejs p.not-implemented{margin-top:3em;margin-left:.3em;color:gray}#conversejs div.delayed .chat-message-them{color:#FB5D50}#conversejs div.delayed .chat-message-me{color:#7EABBB}input.error{border:1px solid red}#conversejs .conn-feedback.error{color:red}#conversejs .chat-message-error{color:#76797C;font-size:90%;font-weight:400}#conversejs .chat-head .avatar{float:left;margin-right:6px}#conversejs div.chat-title{color:#fff;font-weight:700;line-height:15px;display:block;margin-top:2px;margin-right:20px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;text-shadow:rgba(0,0,0,.51)0 -1px 0;height:1em}#conversejs div.chat-title a{color:#fff}#conversejs .chat-head-chatbox,#conversejs .chat-head-chatroom{background:linear-gradient(top,rgba(206,220,231,1)0,rgba(79,106,114,1)100%);height:35px;position:relative}#conversejs p.chatroom-topic,#conversejs p.user-custom-message{font-size:80%;font-style:italic;height:1.3em;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;margin:0}#conversejs div.chat-head-chatbox a.user-custom-message{color:#fff}#conversejs .activated{display:block!important}#conversejs a.subscribe-to-user{padding-left:2em;font-weight:700}dl.add-converse-contact{margin:0 0 0 .5em}#conversejs .fancy-dropdown{border:1px solid #ddd;height:22px}#conversejs .fancy-dropdown a.choose-xmpp-status{width:155px}#conversejs .fancy-dropdown a.choose-xmpp-status,#conversejs .fancy-dropdown a.toggle-xmpp-contact-form{text-shadow:0 1px 0 rgba(255,255,255,1);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;display:inline}#conversejs .fancy-dropdown a.toggle-xmpp-contact-form span{float:left}#conversejs .choose-xmpp-status span{padding-right:5px;padding-left:5px;float:left}#conversejs #fancy-xmpp-status-select a.change-xmpp-status-message{float:right;clear:right;height:22px;width:12px;padding:0 5px 0 0;color:#4f4f4f}#conversejs ul#found-users{padding:10px 0 5px 5px;border:0}#conversejs form.search-xmpp-contact{margin:0;padding:0 0 5px 5px}#conversejs form.search-xmpp-contact input{width:8em}#conversejs .controlbox-head{margin:0;color:#FFF;border-top-right-radius:4px;border-top-left-radius:4px;height:35px;clear:right;background-color:#5390C8;padding:3px 0 0}#conversejs .chat-head-message-count{font-weight:700;position:absolute;left:-6px;top:-6px;background:-webkit-gradient(linear,left top,left bottom,color-stop(0.35,#f6f6f6),color-stop(1,grey));background:-moz-linear-gradient(center top,#ff0 5%,#f6f6f6 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='yellow', endColorstr='#f6f6f6');border:3px solid #4f6a72;text-shadow:1px 1px 0 #ccc;color:#8b0000;border-radius:20%;padding:2px 10px;font-size:18px;text-align:center;display:none}#conversejs .chat-head-chatroom .chat-head-message-count{border:3px solid #2D617A}#conversejs a.close-chatbox-button,#conversejs a.configure-chatroom-button,#conversejs a.toggle-chatbox-button{font-size:10px;padding:3px 3px 2px;margin-right:3px;cursor:pointer;float:right;-moz-box-shadow:inset 0 1px 0 0 #fff;-webkit-box-shadow:inset 0 1px 0 0 #fff;box-shadow:inset 0 1px 0 0 #fff;background:-webkit-gradient(linear,left top,left bottom,color-stop(0.05,#fff),color-stop(1,#f6f6f6));background:-moz-linear-gradient(center top,#fff 5%,#f6f6f6 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#f6f6f6');-moz-border-radius:6px;-webkit-border-radius:6px;border-radius:6px;border:1px solid #888;display:inline-block;color:#666!important;text-decoration:none;text-shadow:1px 1px 0 #fff}#conversejs a.close-chatbox-button:active,#conversejs a.configure-chatroom-button:active,#conversejs a.toggle-chatbox-button:active{position:relative;top:1px}#conversejs .controlbox-pane dt{margin:0;padding-top:.5em}#conversejs .chatroom-form-container{height:100%;color:#666;overflow-y:auto;border-bottom-right-radius:4px;border-bottom-left-radius:4px}#conversejs .chatroom-form{background:#fff;font-size:12px;padding:10px 5px}#conversejs .chat-body p{font-size:14px;color:#666;padding:5px;margin:0}#conversejs .chatroom-form legend{font-size:14px;font-weight:700;margin-bottom:5px}#conversejs .chatroom-form label{font-weight:700;display:block;clear:both}#conversejs .chatroom-form label input,#conversejs .chatroom-form label select{float:right}#conversejs #converse-roster dd.odd{background-color:#DCEAC5}#conversejs #converse-roster dd.current-xmpp-contact{clear:both}#conversejs #converse-roster dd.current-xmpp-contact span{font-size:16px;float:left;color:#4f4f4f}#conversejs #converse-roster dd.requesting-xmpp-contact button{margin-left:.5em}#conversejs #converse-roster dd a,#conversejs #converse-roster dd span{text-shadow:0 1px 0 rgba(250,250,250,1);display:inline-block;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}#conversejs #converse-roster dd span{padding:2px 5px 0 0}#conversejs #converse-roster{overflow-y:auto;overflow-x:hidden;width:100%;position:relative;margin:.5em 0 0;height:-moz-calc(100% - 70px);height:-o-calc(100% - 70px);height:calc(100% - 70px)}#conversejs dd.available-chatroom{overflow-x:hidden;text-overflow:ellipsis;white-space:nowrap;width:175px}#conversejs dd.available-chatroom a.open-room{width:148px}#conversejs #available-chatrooms dt,#conversejs #converse-roster dt{font-weight:400;font-size:13px;color:#666;border:none;padding:.3em 0 0 .5em;text-shadow:0 1px 0 rgba(250,250,250,1)}#conversejs #converse-roster dt{display:none}#conversejs #converse-roster dd,#conversejs dd.available-chatroom{font-weight:700;border:none;display:block;padding:0 0 0 .5em;color:#666;text-shadow:0 1px 0 rgba(250,250,250,1)}#conversejs .room-info{font-size:11px;font-style:normal;font-weight:400}#conversejs li.room-info{display:block;margin-left:5px}#conversejs div.room-info{clear:left}#conversejs p.room-info{margin:0;padding:0;display:block;white-space:normal}#conversejs a.room-info{width:22px;height:22px;float:right;display:none;clear:right}#conversejs a.open-room{float:left;white-space:nowrap;text-overflow:ellipsis;overflow-x:hidden}#conversejs dd.available-chatroom:hover a.room-info{display:inline-block;margin-top:3px;font-size:15px}#conversejs #converse-roster dd a.remove-xmpp-contact{line-height:21px;float:right;width:22px;margin:0;display:none;color:#4f4f4f}#conversejs #converse-roster dd:hover a.remove-xmpp-contact{display:inline-block}#conversejs #converse-roster a.open-chat{line-height:21px;width:85%}#conversejs #converse-roster dd.pending-xmpp-contact:hover span,#conversejs #converse-roster dd:hover a.open-chat{width:70%}#conversejs .chatbox,#conversejs .chatroom{height:25px;float:right;margin-right:15px;display:block}#conversejs .chatbox{width:200px}#conversejs .chatroom{width:300px}#conversejs .controlbox-pane{padding:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}#conversejs .controlbox-pane dd{margin-left:0;margin-bottom:0;padding:1em}#conversejs .controlbox-pane dd.odd{background-color:#DCEAC5}#conversejs form#converse-login{background:#fff;padding:2em 0 .3em .5em}#conversejs form#converse-login input{display:block;width:90%}#conversejs form#converse-login .login-submit{margin-top:1em;width:auto}#conversejs form.set-xmpp-status{background:0 0;margin:none;padding:none}#conversejs form.add-chatroom{background:0 0;padding:3px}#conversejs form.add-chatroom input[type=text]{width:95%;margin:3px}#conversejs form.add-chatroom input[type=button],#conversejs form.add-chatroom input[type=submit]{width:48%}select#select-xmpp-status{float:right;margin-right:.5em}#conversejs .chat-head #controlbox-tabs{text-align:center;display:inline;overflow:hidden;font-size:12px;list-style-type:none}#conversejs .chat-head #controlbox-tabs li{float:left;list-style:none;padding-left:0;text-shadow:#fff 0 1px 0;width:38%}#conversejs ul#controlbox-tabs li a{display:block;font-size:12px;height:34px;line-height:34px;margin:0;text-align:center;text-decoration:none;border-top-right-radius:5px;border-top-left-radius:5px;color:#666;text-shadow:0 1px 0 rgba(250,250,250,1)}#conversejs .chat-head #controlbox-tabs li a:hover{color:#000}#conversejs .chat-head #controlbox-tabs li a{background-color:#fff;box-shadow:inset 0 4px 12px rgba(0,0,0,.3);border-bottom:1px solid #CCC}#conversejs ul#controlbox-tabs a.current,#conversejs ul#controlbox-tabs a.current:hover{box-shadow:none;border-bottom:0;height:35px;cursor:default;color:#666}#conversejs div#chatrooms,#conversejs div#login-dialog,#conversejs div#settings,#conversejs div#users{border:0;font-size:14px;background-color:#fff;border-bottom-right-radius:4px;border-bottom-left-radius:4px;width:100%;height:-moz-calc(100% - 38px);height:-o-calc(100% - 38px);height:calc(100% - 38px);overflow-y:hidden;position:absolute}#conversejs div#chatrooms{overflow-y:auto}#conversejs form.sendXMPPMessage{background:#fff;border:0;border-top:1px solid #BBB;padding:0;margin:0;position:relative;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-moz-background-clip:padding;-webkit-background-clip:padding-box;background-clip:padding-box;border-top-left-radius:0;border-top-right-radius:0;width:200px;height:75px}#conversejs .chatroom form.sendXMPPMessage{-webkit-border-bottom-right-radius:0;border-bottom-right-radius:0}#conversejs .chat-textarea{box-sizing:border-box;-moz-box-sizing:border-box;border:0;width:100%;padding:3px;border-radius:4px;resize:none;height:55px}#conversejs ul.chat-toolbar{font-size:14px;margin:0;padding:0 5px 0 0;width:195px;float:right;display:inline-block;height:20px}#conversejs .chat-toolbar .toggle-otr{float:right}#conversejs .chat-toolbar .toggle-call,#conversejs .chat-toolbar ul li a{color:#4f4f4f}#conversejs .chat-toolbar ul li a:hover{color:#8f2831}#conversejs .chat-toolbar ul{display:none;font-size:12px;position:absolute;bottom:100%;margin-bottom:1px;right:0;background:#fff;box-shadow:0 -1px 2px 0 rgba(0,0,0,.4)}#conversejs .chat-toolbar ul li{position:relative;list-style:none;cursor:pointer}#conversejs .chat-toolbar .toggle-smiley{padding-left:5px}#conversejs .chat-toolbar .toggle-smiley ul li{font-size:14px;padding:5px;z-index:98}#conversejs .chat-toolbar .toggle-otr ul li{background-color:#fff;display:block;z-index:99}#conversejs .chat-toolbar ul li:hover{background-color:#eee}#conversejs .chat-toolbar .toggle-otr ul li a{transition:background-color .2s ease-in-out;-webkit-transition:background-color .2s ease-in-out;-moz-transition:background-color .2s ease-in-out;display:block;padding:1px;text-decoration:none}#conversejs .chat-toolbar-text{font-size:12px;padding-right:3px}#conversejs .unencrypted,#conversejs .unencrypted a{color:#8f2831}#conversejs .unverified,#conversejs .unverified a{color:#cf5300}#conversejs .private,#conversejs .private a{color:#4B7003}#conversejs ul.chat-toolbar li{display:inline-block;list-style:none;padding:0 3px;cursor:pointer;margin-top:1px}#conversejs ul.chat-toolbar li:hover{cursor:pointer}#conversejs form#set-custom-xmpp-status{float:left;padding:0}#conversejs .chat-textarea-chatbox-selected{border:1px solid #578308;margin:0}#conversejs .chat-textarea-chatroom-selected{border:2px solid #2D617A;margin:0}#conversejs #set-custom-xmpp-status button{padding:1px 2px 1px 1px}#conversejs #controlbox{display:none}#conversejs #controlbox div.xmpp-status{display:inline}#conversejs .chatbox dl.dropdown{margin:.5em;background-color:#f0f0f0}#conversejs .chatbox .dropdown dd,#conversejs .dropdown dt,#conversejs .dropdown ul{margin:0;padding:0}#conversejs .chatbox .dropdown dd{position:relative}input.custom-xmpp-status{width:124px}#conversejs form.add-xmpp-contact{background:0 0;padding:5px}#conversejs form.add-xmpp-contact input{width:108px}#conversejs .chatbox .dropdown dt a span{cursor:pointer;display:block;padding:4px 7px 0 5px;color:#4f4f4f}#conversejs .chatbox .dropdown dd ul{padding:5px 0;list-style:none;position:absolute;left:0;top:0;border:1px solid #ddd;border-top:0;width:99%;z-index:21;background-color:#f0f0f0}#conversejs .chatbox .dropdown li{list-style:none;padding-left:0}#conversejs .chatbox .dropdown a{height:22px;width:148px;display:inline-block;line-height:24px}#conversejs .chatbox .dropdown dd ul li:hover{background-color:#bed6e5}#conversejs .xmpp-status-menu li a{width:100%}#conversejs .xmpp-status-menu li a span{padding:0 5px;color:#4f4f4f}#conversejs .set-xmpp-status .dropdown dd ul{z-index:22}#conversejs .box-flyout{background:#fff;position:absolute;display:block;bottom:1px;box-shadow:1px 1px 5px 1px rgba(0,0,0,.4);border-radius:4px;height:324px}#conversejs .box-flyout.minimized{height:auto}#conversejs .chatbox .box-flyout{width:200px}#conversejs .chatroom .box-flyout{width:300px}#conversejs .dragresize{position:absolute;width:200px;height:5px;background:0 0;border:0;top:0;margin-left:0;cursor:n-resize;z-index:20}
\ No newline at end of file
...@@ -568,13 +568,7 @@ span.spinner.hor_centered { ...@@ -568,13 +568,7 @@ span.spinner.hor_centered {
margin-right: 5px; margin-right: 5px;
color: white; color: white;
} }
#conversejs #offscreen-chatboxes { #conversejs #trimmed-chatboxes .box-flyout {
float: left;
height: 25px;
margin-left: 5px;
display: block;
}
#conversejs #offscreen-chatboxes .box-flyout {
position: absolute; position: absolute;
display: block; display: block;
bottom: 1px; bottom: 1px;
...@@ -582,15 +576,16 @@ span.spinner.hor_centered { ...@@ -582,15 +576,16 @@ span.spinner.hor_centered {
border-radius: 4px; border-radius: 4px;
height: auto; height: auto;
} }
#conversejs #offscreen-chatboxes .box-flyout .chat-head { #conversejs #trimmed-chatboxes .box-flyout .chat-head {
font-size: 100%; font-size: 100%;
border-radius: 4px; border-radius: 4px;
padding: 3px 0 0 5px; padding: 3px 0 0 5px;
margin: 0 0 2px 2px; margin: 0 0 2px 2px;
box-shadow: 1px 3px 5px 3px rgba(0, 0, 0, 0.4); box-shadow: 1px 3px 5px 3px rgba(0, 0, 0, 0.4);
height: 24px; height: 24px;
width: 130px;
} }
#conversejs #offscreen-chatboxes .chat-head-chatroom { #conversejs #trimmed-chatboxes .chat-head-chatroom {
width: 100px; width: 100px;
} }
#conversejs #toggle-controlbox { #conversejs #toggle-controlbox {
...@@ -745,10 +740,11 @@ input.error { ...@@ -745,10 +740,11 @@ input.error {
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;
} }
#conversejs .chat-title a { #conversejs .chat-title a {
color: white; color: white;
width: 100%;
display: block;
} }
#conversejs .chat-head-chatbox, #conversejs .chat-head-chatbox,
#conversejs .chat-head-chatroom { #conversejs .chat-head-chatroom {
...@@ -836,23 +832,20 @@ dl.add-converse-contact { ...@@ -836,23 +832,20 @@ dl.add-converse-contact {
#conversejs .chat-head-message-count { #conversejs .chat-head-message-count {
font-weight: bold; font-weight: bold;
position: absolute; position: absolute;
left: -6px; left: -5px;
top: -6px; top: 2px;
background: -webkit-gradient(linear, left top, left bottom, color-stop(0.35, #f6f6f6), color-stop(1, #808080)); background: -webkit-gradient(linear, left top, left bottom, color-stop(0.35, #f6f6f6), color-stop(1, #808080));
background: -moz-linear-gradient(center top, #ffff00 5%, #f6f6f6 100%); background: -moz-linear-gradient(center top, #ffff00 5%, #f6f6f6 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='yellow', endColorstr='#f6f6f6'); filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='yellow', endColorstr='#f6f6f6');
border: 3px solid #4f6a72; border: 1px solid;
text-shadow: 1px 1px 0 #ccc; text-shadow: 1px 1px 0 #ccc;
color: darkred; color: darkred;
border-radius: 20%; border-radius: 20%;
padding: 2px 10px; padding: 2px 4px;
font-size: 18px; font-size: 15px;
text-align: center; text-align: center;
display: none; display: none;
} }
#conversejs .chat-head-chatroom .chat-head-message-count {
border: 3px solid #2D617A;
}
#conversejs a.configure-chatroom-button, #conversejs a.configure-chatroom-button,
#conversejs a.toggle-chatbox-button, #conversejs a.toggle-chatbox-button,
#conversejs a.close-chatbox-button { #conversejs a.close-chatbox-button {
...@@ -1040,6 +1033,7 @@ dl.add-converse-contact { ...@@ -1040,6 +1033,7 @@ dl.add-converse-contact {
#conversejs #converse-roster dd.pending-xmpp-contact:hover span { #conversejs #converse-roster dd.pending-xmpp-contact:hover span {
width: 70%; width: 70%;
} }
#conversejs #trimmed-chatboxes,
#conversejs .chatbox, #conversejs .chatbox,
#conversejs .chatroom { #conversejs .chatroom {
height: 25px; height: 25px;
...@@ -1047,6 +1041,9 @@ dl.add-converse-contact { ...@@ -1047,6 +1041,9 @@ dl.add-converse-contact {
margin-right: 15px; margin-right: 15px;
display: block; display: block;
} }
#conversejs #trimmed-chatboxes {
width: 130px;
}
#conversejs .chatbox { #conversejs .chatbox {
width: 200px; width: 200px;
} }
......
...@@ -9,6 +9,7 @@ Changelog ...@@ -9,6 +9,7 @@ Changelog
2. Configuration options for the chat toolbar have changed. 2. Configuration options for the chat toolbar have changed.
Please refer to the `relevant documentation http://devbox:8890/docs/html/index.html#visible-toolbar-buttons`_. Please refer to the `relevant documentation http://devbox:8890/docs/html/index.html#visible-toolbar-buttons`_.
* No initial HTML markup is now needed in the document body for converse.js to work. [jcbrand]
* All date handling is now done with moment.js. [jcbrand] * All date handling is now done with moment.js. [jcbrand]
* Add a new toolbar button for clearing chat messages. [jcbrand] * Add a new toolbar button for clearing chat messages. [jcbrand]
* Chat boxes and rooms can now be resized vertically. [jcbrand] * Chat boxes and rooms can now be resized vertically. [jcbrand]
......
...@@ -52,13 +52,6 @@ bottom of your page (after the closing *</body>* element). ...@@ -52,13 +52,6 @@ bottom of your page (after the closing *</body>* element).
}); });
}); });
Finally, Converse.js requires a special snippet of HTML markup to be included in your page:
::
<div id="conversejs"></div>
The `index.html <https://github.com/jcbrand/converse.js/blob/master/index.html>`_ file inside the The `index.html <https://github.com/jcbrand/converse.js/blob/master/index.html>`_ file inside the
Converse.js repository serves as a nice usable example of this. Converse.js repository serves as a nice usable example of this.
......
...@@ -13,6 +13,11 @@ ...@@ -13,6 +13,11 @@
<link type="text/css" rel="stylesheet" media="screen" href="css/theme.css" /> <link type="text/css" rel="stylesheet" media="screen" href="css/theme.css" />
<link type="text/css" rel="stylesheet" media="screen" href="css/converse.css" /> <link type="text/css" rel="stylesheet" media="screen" href="css/converse.css" />
<script data-main="main" src="components/requirejs/require.js"></script> <script data-main="main" src="components/requirejs/require.js"></script>
<script src="../components/jquery/dist/jquery.min.js"></script>
<script src="http://netdna.bootstrapcdn.com/bootstrap/3.0.3/js/bootstrap.min.js"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/jquery-easing/1.3/jquery.easing.min.js"></script>
<script src="js/init.js"></script>
</head> </head>
<body id="page-top" data-spy="scroll" data-target=".navbar-custom"> <body id="page-top" data-spy="scroll" data-target=".navbar-custom">
...@@ -209,15 +214,6 @@ ...@@ -209,15 +214,6 @@
</div> </div>
</div> </div>
</section> </section>
<!-- Core JavaScript Files -->
<script src="../components/jquery/dist/jquery.min.js"></script>
<script src="http://netdna.bootstrapcdn.com/bootstrap/3.0.3/js/bootstrap.min.js"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/jquery-easing/1.3/jquery.easing.min.js"></script>
<!-- Custom Theme JavaScript -->
<script src="js/init.js"></script>
<div id="conversejs"></div>
</body> </body>
<script type="text/javascript"> <script type="text/javascript">
......
...@@ -598,14 +598,7 @@ span.spinner.hor_centered { ...@@ -598,14 +598,7 @@ span.spinner.hor_centered {
color: white; color: white;
} }
#conversejs #offscreen-chatboxes { #conversejs #trimmed-chatboxes .box-flyout {
float: left;
height: 25px;
margin-left: 5px;
display: block;
}
#conversejs #offscreen-chatboxes .box-flyout {
position: absolute; position: absolute;
display: block; display: block;
bottom: 1px; bottom: 1px;
...@@ -614,16 +607,17 @@ span.spinner.hor_centered { ...@@ -614,16 +607,17 @@ span.spinner.hor_centered {
height: auto; height: auto;
} }
#conversejs #offscreen-chatboxes .box-flyout .chat-head { #conversejs #trimmed-chatboxes .box-flyout .chat-head {
font-size: 100%; font-size: 100%;
border-radius: 4px; border-radius: 4px;
padding: 3px 0 0 5px; padding: 3px 0 0 5px;
margin: 0 0 2px 2px; margin: 0 0 2px 2px;
box-shadow: 1px 3px 5px 3px rgba(0,0,0,0.4); box-shadow: 1px 3px 5px 3px rgba(0,0,0,0.4);
height: 24px; height: 24px;
width: 130px;
} }
#conversejs #offscreen-chatboxes .chat-head-chatroom { #conversejs #trimmed-chatboxes .chat-head-chatroom {
width: 100px; width: 100px;
} }
...@@ -808,11 +802,12 @@ input.error { ...@@ -808,11 +802,12 @@ input.error {
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;
} }
#conversejs .chat-title a { #conversejs .chat-title a {
color: white; color: white;
width: 100%;
display: block;
} }
#conversejs .chat-head-chatbox, #conversejs .chat-head-chatbox,
...@@ -916,25 +911,21 @@ dl.add-converse-contact { ...@@ -916,25 +911,21 @@ dl.add-converse-contact {
#conversejs .chat-head-message-count { #conversejs .chat-head-message-count {
font-weight: bold; font-weight: bold;
position: absolute; position: absolute;
left: -6px; left: -5px;
top: -6px; top: 2px;
background: -webkit-gradient(linear, left top, left bottom, color-stop(0.35, #f6f6f6), color-stop(1, grey) ); background: -webkit-gradient(linear, left top, left bottom, color-stop(0.35, #f6f6f6), color-stop(1, grey) );
background: -moz-linear-gradient(center top, yellow 5%, #f6f6f6 100%); background: -moz-linear-gradient(center top, yellow 5%, #f6f6f6 100%);
filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='yellow', endColorstr='#f6f6f6'); filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='yellow', endColorstr='#f6f6f6');
border: 3px solid rgb(79, 106, 114); border: 1px solid;
text-shadow: 1px 1px 0 #ccc; text-shadow: 1px 1px 0 #ccc;
color: darkred; color: darkred;
border-radius: 20%; border-radius: 20%;
padding: 2px 10px; padding: 2px 4px;
font-size: 18px; font-size: 15px;
text-align: center; text-align: center;
display: none; display: none;
} }
#conversejs .chat-head-chatroom .chat-head-message-count {
border: 3px solid #2D617A;
}
#conversejs a.configure-chatroom-button, #conversejs a.configure-chatroom-button,
#conversejs a.toggle-chatbox-button, #conversejs a.toggle-chatbox-button,
#conversejs a.close-chatbox-button { #conversejs a.close-chatbox-button {
...@@ -1152,6 +1143,7 @@ dl.add-converse-contact { ...@@ -1152,6 +1143,7 @@ dl.add-converse-contact {
width: 70%; width: 70%;
} }
#conversejs #trimmed-chatboxes,
#conversejs .chatbox, #conversejs .chatbox,
#conversejs .chatroom { #conversejs .chatroom {
height: 25px; height: 25px;
...@@ -1160,6 +1152,10 @@ dl.add-converse-contact { ...@@ -1160,6 +1152,10 @@ dl.add-converse-contact {
display: block; display: block;
} }
#conversejs #trimmed-chatboxes {
width: 130px;
}
#conversejs .chatbox { #conversejs .chatbox {
width: 200px; width: 200px;
} }
......
...@@ -8,6 +8,7 @@ config = { ...@@ -8,6 +8,7 @@ config = {
"underscore": "components/underscore/underscore", "underscore": "components/underscore/underscore",
"backbone": "components/backbone/backbone", "backbone": "components/backbone/backbone",
"backbone.localStorage": "components/backbone.localStorage/backbone.localStorage", "backbone.localStorage": "components/backbone.localStorage/backbone.localStorage",
"backbone.overview": "components/backbone.overview/backbone.overview",
"text": 'components/requirejs-text/text', "text": 'components/requirejs-text/text',
"tpl": 'components/requirejs-tpl-jcbrand/tpl', "tpl": 'components/requirejs-tpl-jcbrand/tpl',
"converse-templates": "src/templates", "converse-templates": "src/templates",
......
...@@ -373,27 +373,72 @@ ...@@ -373,27 +373,72 @@
</div> </div>
</div> </div>
<div id="offscreen-chatboxes"> <div id="trimmed-chatboxes">
<div class="box-flyout"> <div class="box-flyout">
<div class="chat-head chat-head-chatroom"> <div class="chat-head chat-head-chatroom">
<a class="close-chatbox-button icon-close"></a> <a class="close-chatbox-button icon-close"></a>
<div class="chat-title"> Restricted Chatroom</div> <div class="chat-title">
<a href="#" class="restore-chat" title="Click to maximize this chat">
<div class="chat-head-message-count" style="display:block">3</div>
Restricted Chatroom
</a>
</div>
</div> </div>
<div class="chat-head chat-head-chatbox"> <div class="chat-head chat-head-chatbox">
<a class="close-chatbox-button icon-close"></a> <a class="close-chatbox-button icon-close"></a>
<div class="chat-title"> <div class="chat-title">
<a href="http://opkode.com" target="_blank" class="user"> <a href="#" class="restore-chat" title="Click to maximize this chat">
<div class="chat-head-message-count" style="display:block">42</div>
JC Brand JC Brand
</a> </a>
</div> </div>
</div> </div>
<div class="chat-head chat-head-chatroom"> <div class="chat-head chat-head-chatroom">
<a class="close-chatbox-button icon-close"></a> <a class="close-chatbox-button icon-close"></a>
<div class="chat-title"> My Chatroom</div> <div class="chat-title">
<a href="#" class="restore-chat" title="Click to maximize this chat">
My Chatroom
</a>
</div>
</div>
<div class="chat-head chat-head-chatbox"><a class="close-chatbox-button icon-close"></a>
<div class="chat-title">
<a href="#" class="restore-chat" title="Click to maximize this chat">
Annegreet Gomez
</a>
</div>
</div>
<div class="chat-head chat-head-chatbox"><a class="close-chatbox-button icon-close"></a>
<div class="chat-title">
<a href="#" class="restore-chat" title="Click to maximize this chat">
<div class="chat-head-message-count" style="display:block">842</div>
Asmaa Haakman
</a>
</div>
</div>
<div class="chat-head chat-head-chatbox"><a class="close-chatbox-button icon-close"></a>
<div class="chat-title">
<a href="#" class="restore-chat" title="Click to maximize this chat">
Candice van der Knijff
</a>
</div>
</div>
<div class="chat-head chat-head-chatbox"><a class="close-chatbox-button icon-close"></a>
<div class="chat-title">
<a href="#" class="restore-chat" title="Click to maximize this chat">
Laura Grunewald
</a>
</div>
</div>
<div class="chat-head chat-head-chatbox"><a class="close-chatbox-button icon-close"></a>
<div class="chat-title">
<a href="#" class="restore-chat">
Lena Grunewald
</a>
</div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<script> <script>
...@@ -458,13 +503,7 @@ $(document).ready(function () { ...@@ -458,13 +503,7 @@ $(document).ready(function () {
$('.toggle-chatbox-button').click(function(ev) { $('.toggle-chatbox-button').click(function(ev) {
var $grandparent = $(ev.target).parent().parent().parent(); var $grandparent = $(ev.target).parent().parent().parent();
$grandparent.find('.chat-body').slideToggle('fast'); $grandparent.fadeOut('fast');
var flyout = $grandparent.find('.box-flyout');
if (flyout.hasClass('minimized')) {
flyout.removeClass('minimized');
} else {
flyout.addClass('minimized');
}
}); });
// Clickable Dropdown // Clickable Dropdown
......
<!DOCTYPE html>
<html lang="en">
<head>
<title id="pageTitle">Converse.js: Mockup of minimized chats</title>
<meta charset="utf-8">
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta name="description" content="Converse.js: Chat Client for Websites" />
<link type="text/css" href="../css/theme.css" rel="stylesheet" media="screen" />
<link type="text/css" href="../css/converse.css" rel="stylesheet" media="screen" />
<script src="../components/jquery/dist/jquery.min.js"></script>
</head>
<body>
<!-- HEADER -->
<div id="header_wrap" class="outer">
<header class="inner">
<h1 id="project_title"><a href="http://conversejs.org">Converse.js</a></h1>
<h2 id="project_tagline">Static Mockup</h2>
</header>
</div>
<div id="conversejs">
<div class="chatbox" id="37c0c87392010303765fe36b05c0967d62c6b70f">
<div class="box-flyout minimized">
<div class="dragresize dragresize-tm"></div>
<div class="chat-head chat-head-chatbox">
<div class="chat-head-message-count" style="display:block">3</div>
<a class="close-chatbox-button icon-close"></a>
<a class="toggle-chatbox-button icon-minus"></a>
<canvas height="31px" width="31px" class="avatar" style="background-color: black"></canvas>
<div class="chat-title">
<a href="http://opkode.com" target="_blank" class="user">
JC Brand
</a>
</div>
<p class="user-custom-message" title="10000ft in the air">10000ft in the air</p>
</div>
<div class="chat-body" style="display: none">
<div class="chat-content">
<div class="chat-info"><strong>/help</strong>:This is an info message</div>
<div class="chat-error">This is an error message</div>
<div class="chat-message">
<span class="chat-message-me">09:35 me:&nbsp;</span>
<span class="chat-message-content">
Hello world
<span class="icon-smiley"></span>
</span>
</div>
<div class="chat-message ">
<span class="chat-message-them">19:25 Benedict-John:&nbsp;</span>
<span class="chat-message-content">Dagsê</span>
</div>
<div class="chat-message">
<span class="chat-message-me">19:39 me:&nbsp;</span>
<span class="chat-message-content">This is a relatively long message to check that wrapping works as expected.</span>
</div>
<div class="chat-message">
<span class="chat-message-me">19:42 me:&nbsp;</span>
<span class="chat-message-content">Supercalifragilisticexpialidociousstillnotlongenough</span>
</div>
<div class="chat-event">JC Brand is busy</div>
<div class="chat-message ">
<span class="chat-message-me">19:43 me:&nbsp;</span>
<span class="chat-message-content">Another message to check that scrolling works.</span>
</div>
</div>
<form class="sendXMPPMessage" action="" method="post">
<ul class="chat-toolbar no-text-select">
<li class="toggle-smiley icon-happy" title="Insert a smilery">
<ul>
<li><a class="icon-smiley" href="#" data-emoticon=":)"></a></li>
<li><a class="icon-wink" href="#" data-emoticon=";)"></a></li>
<li><a class="icon-grin" href="#" data-emoticon=":D"></a></li>
<li><a class="icon-tongue" href="#" data-emoticon=":P"></a></li>
<li><a class="icon-cool" href="#" data-emoticon="8)"></a></li>
<li><a class="icon-evil" href="#" data-emoticon=">:)"></a></li>
<li><a class="icon-confused" href="#" data-emoticon=":S"></a></li>
<li><a class="icon-wondering" href="#" data-emoticon=":\"></a></li>
<li><a class="icon-angry" href="#" data-emoticon=">:("></a></li>
<li><a class="icon-sad" href="#" data-emoticon=":("></a></li>
<li><a class="icon-shocked" href="#" data-emoticon=":O"></a></li>
<li><a class="icon-thumbs-up" href="#" data-emoticon="(^.^)b"></a></li>
<li><a class="icon-heart" href="#" data-emoticon="<3"></a></li>
</ul>
</li>
<li class="toggle-otr unencrypted" title="Turn on 'off-the-record' chat encryption">
<span class="chat-toolbar-text">unencrypted</span>
<span class="icon-unlocked"></span>
<ul>
<li><a href="#">Start private conversation</a></li>
<li><a href="#">End private conversation</a></li>
<li><a href="#">Authenticate buddy</a></li>
<li><a href="http://www.cypherpunks.ca/otr/help/3.2.0/levels.php" target="_blank">What's this?</a></li>
</ul>
</li>
</ul>
<textarea type="text" class="chat-textarea" placeholder="Personal message"></textarea>
</form>
</div>
</div>
</div>
<div class="chatroom" id="4a77380f1cd9d392627b0e1469688f9ca44e9392">
<div class="box-flyout minimized">
<div class="dragresize dragresize-tm"></div>
<div class="chat-head chat-head-chatroom">
<div class="chat-head-message-count" style="display:block">42</div>
<a class="close-chatbox-button icon-close"></a>
<a class="toggle-chatbox-button icon-minus"></a>
<a class="configure-chatroom-button icon-wrench" style=""></a>
<div class="chat-title"> Chatroom </div>
<p class="chatroom-topic">May the force be with you</p>
</div>
<div class="chat-body" style="display: none">
<div class="chat-area">
<div class="chat-content">
<time class="chat-date" datetime="2013-06-04T00:00:00.000Z">Tue Jun 04 2013</time>
<div class="chat-message ">
<span class="chat-message-room">18:50 luke:&nbsp;</span>
<span class="chat-message-content">leia: hi :)</span>
</div>
<div class="chat-message ">
<span class="chat-message-room">19:40 leia:&nbsp;</span>
<span class="chat-message-content">
I'll be gone for a while, will be back in about an hour</span>
</div>
<div class="chat-message ">
<span class="chat-message-room">19:40 Obi-wan Kenobi, Jedi Master:&nbsp;</span>
<span class="chat-message-content">
I'll be gone for a while, will be back in about an hour</span>
</div>
<div class="chat-message">
<span class="chat-message-me">19:42 me:&nbsp;</span>
<span class="chat-message-content">Supercalifragilisticexpialidociousstillnotlongenough</span>
</div>
<div class="chat-message ">
<span class="chat-message-room">19:43 Obi-wan Kenobi, Jedi Master:&nbsp;</span>
<span class="chat-message-content">Another message to check that scrolling works.</span>
</div>
</div>
<form class="sendXMPPMessage" action="" method="post">
<ul class="chat-toolbar no-text-select">
<li class="toggle-smiley icon-happy" title="Insert a smilery">
<ul>
<li><a class="icon-smiley" href="#" data-emoticon=":)"></a></li>
<li><a class="icon-wink" href="#" data-emoticon=";)"></a></li>
<li><a class="icon-grin" href="#" data-emoticon=":D"></a></li>
<li><a class="icon-tongue" href="#" data-emoticon=":P"></a></li>
<li><a class="icon-cool" href="#" data-emoticon="8)"></a></li>
<li><a class="icon-evil" href="#" data-emoticon=">:)"></a></li>
<li><a class="icon-confused" href="#" data-emoticon=":S"></a></li>
<li><a class="icon-wondering" href="#" data-emoticon=":\"></a></li>
<li><a class="icon-angry" href="#" data-emoticon=">:("></a></li>
<li><a class="icon-sad" href="#" data-emoticon=":("></a></li>
<li><a class="icon-shocked" href="#" data-emoticon=":O"></a></li>
<li><a class="icon-thumbs-up" href="#" data-emoticon="(^.^)b"></a></li>
<li><a class="icon-heart" href="#" data-emoticon="<3"></a></li>
</ul>
</li>
</ul>
<textarea type="text" class="chat-textarea" placeholder="Message"></textarea>
</form>
</div>
<div class="participants">
<ul class="participant-list">
<li class="participant" title="This user can send messages in this room">Obi-wan Kenobi, Jedi Master</li>
<li class="participant" title="This user can send messages in this room">jabber the hut</li>
<li class="participant" title="This user can send messages in this room">leia</li>
<li class="moderator" title="This user is a moderator">luke</li>
</ul>
</div>
</div>
</div>
</div>
</div>
<script>
$(document).ready(function () {
$('a[href=#chatrooms]').click(function (ev) {
switchTab(ev);
});
$('a[href=#users]').click(function (ev) {
switchTab(ev);
});
$("a.choose-xmpp-status").click(function (ev) {
ev.preventDefault();
$(ev.target).parent().parent().siblings('dd').find('ul').toggle('fast');
});
$("a.change-xmpp-status-message").click(function (ev) {
ev.preventDefault();
var form = ''+
'<form id="set-custom-xmpp-status">' +
'<input type="text" class="custom-xmpp-status"I am online"'+
'placeholder="I am online"/>' +
'<button type="submit">Save</button>' +
'</form>';
$(ev.target).closest('.xmpp-status').replaceWith(form);
$(ev.target).closest('.custom-xmpp-status').focus().focus();
});
$('.toggle-xmpp-contact-form').click(function (ev) {
ev.preventDefault();
$(ev.target).parent().parent().find('.search-xmpp').toggle('fast', function () {
if ($(this).is(':visible')) {
$(this).find('input.username').focus();
}
});
});
var switchTab = function (ev) {
ev.preventDefault();
var $tab = $(ev.target),
$sibling = $tab.parent().siblings('li').children('a'),
$tab_panel = $($tab.attr('href')),
$sibling_panel = $($sibling.attr('href'));
$sibling_panel.fadeOut('fast', function () {
$sibling.removeClass('current');
$tab.addClass('current');
$tab_panel.fadeIn('fast', function () {
});
});
}
$(function() {
$('.close-chatbox-button').click(function(ev) {
var $grandparent = $(ev.target).parent().parent().parent();
$grandparent.hide(300, function () {
// Webkit fix
document.getElementById('conversejs').style.display = 'none';
document.getElementById('conversejs').offsetHeight; // no need to store this anywhere, the reference is enough
document.getElementById('conversejs').style.display = 'block';
});
});
$('.toggle-chatbox-button').click(function(ev) {
var $grandparent = $(ev.target).parent().parent().parent();
$grandparent.find('.chat-body').slideToggle(300);
var flyout = $grandparent.find('.box-flyout');
if (flyout.hasClass('minimized')) {
flyout.removeClass('minimized');
} else {
flyout.addClass('minimized');
}
});
// Clickable Dropdown
$('.toggle-otr').click(function(e) {
$('.toggle-otr ul').slideToggle(200);
e.stopPropagation();
});
$('.toggle-smiley').click(function(e) {
$(e.target).find('ul').slideToggle(200);
e.stopPropagation();
});
$(document).click(function() {
if ($('.toggle-otr ul').is(':visible')) {
$('.toggle-otr ul', this).slideUp(200);
}
if ($('.toggle-smiley ul').is(':visible')) {
$('.toggle-smiley ul', this).slideUp(200);
}
});
});
});
</script>
</html>
...@@ -30,11 +30,12 @@ ...@@ -30,11 +30,12 @@
}); });
it("is created when you click on a roster item", $.proxy(function () { it("is created when you click on a roster item", $.proxy(function () {
var i, $el, click, jid, view; var i, $el, click, jid, view, chatboxview;
// openControlBox was called earlier, so the controlbox is // openControlBox was called earlier, so the controlbox is
// visible, but no other chat boxes have been created. // visible, but no other chat boxes have been created.
expect(this.chatboxes.length).toEqual(1); expect(this.chatboxes.length).toEqual(1);
spyOn(this.chatboxviews, 'trimOpenChats'); spyOn(this.chatboxviews, 'trimChats');
expect($("#conversejs .chatbox").length).toBe(1); // Controlbox is open
var online_contacts = this.rosterview.$el.find('dt#xmpp-contacts').siblings('dd.current-xmpp-contact').find('a.open-chat'); var online_contacts = this.rosterview.$el.find('dt#xmpp-contacts').siblings('dd.current-xmpp-contact').find('a.open-chat');
for (i=0; i<online_contacts.length; i++) { for (i=0; i<online_contacts.length; i++) {
...@@ -44,9 +45,59 @@ ...@@ -44,9 +45,59 @@
spyOn(view, 'openChat').andCallThrough(); spyOn(view, 'openChat').andCallThrough();
view.delegateEvents(); // We need to rebind all events otherwise our spy won't be called view.delegateEvents(); // We need to rebind all events otherwise our spy won't be called
$el.click(); $el.click();
chatboxview = this.chatboxviews.get(jid);
expect(view.openChat).toHaveBeenCalled(); expect(view.openChat).toHaveBeenCalled();
expect(this.chatboxes.length).toEqual(i+2); expect(this.chatboxes.length).toEqual(i+2);
expect(this.chatboxviews.trimOpenChats).toHaveBeenCalled(); expect(this.chatboxviews.trimChats).toHaveBeenCalled();
// Check that new chat boxes are created to the left of the
// controlbox (but to the right of all existing chat boxes)
expect($("#conversejs .chatbox").length).toBe(i+2);
expect($("#conversejs .chatbox")[1].id).toBe(chatboxview.model.get('box_id'));
}
}, converse));
it("can be trimmed to conserve space", $.proxy(function () {
var i, $el, click, jid, key, view, chatboxview;
// openControlBox was called earlier, so the controlbox is
// visible, but no other chat boxes have been created.
var trimmed_chatboxes = converse.chatboxviews.trimmed_chatboxes_view;
expect(this.chatboxes.length).toEqual(1);
spyOn(this.chatboxviews, 'trimChats');
spyOn(trimmed_chatboxes, 'onChanged').andCallThrough();
expect($("#conversejs .chatbox").length).toBe(1); // Controlbox is open
// Test that they can be trimmed
var online_contacts = this.rosterview.$el.find('dt#xmpp-contacts').siblings('dd.current-xmpp-contact').find('a.open-chat');
for (i=0; i<online_contacts.length; i++) {
$el = $(online_contacts[i]);
jid = $el.text().replace(' ','.').toLowerCase() + '@localhost';
view = this.rosterview.get(jid);
view.delegateEvents(); // We need to rebind all events otherwise our spy won't be called
$el.click();
expect(this.chatboxviews.trimChats).toHaveBeenCalled();
chatboxview = this.chatboxviews.get(jid);
spyOn(chatboxview, 'hide').andCallThrough();
chatboxview.model.set({'minimized': true});
expect(trimmed_chatboxes.onChanged).toHaveBeenCalled();
expect(chatboxview.hide).toHaveBeenCalled();
trimmedview = trimmed_chatboxes.get(jid);
expect(trimmedview.$el.is(":visible")).toBeTruthy();
}
// Test that they can be maximized again
var chatboxviews = this.chatboxviews.getAll();
var keys = _.keys(chatboxviews);
for (i=0; i<keys.length; i++) {
key = keys[i];
if (key === 'controlbox') {
continue;
}
chatboxview = chatboxviews[key];
trimmedview = trimmed_chatboxes.get(key);
spyOn(chatboxview, 'maximize').andCallThrough();
trimmedview.$("a.restore-chat").click();
expect(trimmed_chatboxes.onChanged).toHaveBeenCalled();
expect(chatboxview.maximize).toHaveBeenCalled();
} }
}, converse)); }, converse));
...@@ -72,14 +123,14 @@ ...@@ -72,14 +123,14 @@
it("can be saved to, and retrieved from, localStorage", $.proxy(function () { it("can be saved to, and retrieved from, localStorage", $.proxy(function () {
spyOn(converse, 'emit'); spyOn(converse, 'emit');
spyOn(this.chatboxviews, 'trimOpenChats'); spyOn(this.chatboxviews, 'trimChats');
runs(function () { runs(function () {
utils.openControlBox(); utils.openControlBox();
}); });
waits(250); waits(250);
runs(function () { runs(function () {
utils.openChatBoxes(6); utils.openChatBoxes(6);
expect(this.chatboxviews.trimOpenChats).toHaveBeenCalled(); expect(this.chatboxviews.trimChats).toHaveBeenCalled();
// We instantiate a new ChatBoxes collection, which by default // We instantiate a new ChatBoxes collection, which by default
// will be empty. // will be empty.
var newchatboxes = new this.ChatBoxes(); var newchatboxes = new this.ChatBoxes();
...@@ -104,8 +155,8 @@ ...@@ -104,8 +155,8 @@
var chatbox = utils.openChatBoxes(1)[0], var chatbox = utils.openChatBoxes(1)[0],
controlview = this.chatboxviews.get('controlbox'), // The controlbox is currently open controlview = this.chatboxviews.get('controlbox'), // The controlbox is currently open
chatview = this.chatboxviews.get(chatbox.get('jid')); chatview = this.chatboxviews.get(chatbox.get('jid'));
spyOn(chatview, 'closeChat').andCallThrough(); spyOn(chatview, 'close').andCallThrough();
spyOn(controlview, 'closeChat').andCallThrough(); spyOn(controlview, 'close').andCallThrough();
spyOn(converse, 'emit'); spyOn(converse, 'emit');
// We need to rebind all events otherwise our spy won't be called // We need to rebind all events otherwise our spy won't be called
...@@ -117,56 +168,60 @@ ...@@ -117,56 +168,60 @@
}); });
waits(250); waits(250);
runs(function () { runs(function () {
expect(controlview.closeChat).toHaveBeenCalled(); expect(controlview.close).toHaveBeenCalled();
expect(converse.emit).toHaveBeenCalledWith('onChatBoxClosed', jasmine.any(Object)); expect(converse.emit).toHaveBeenCalledWith('onChatBoxClosed', jasmine.any(Object));
expect(converse.emit.callCount, 1); expect(converse.emit.callCount, 1);
chatview.$el.find('.close-chatbox-button').click(); chatview.$el.find('.close-chatbox-button').click();
}); });
waits(250); waits(250);
runs(function () { runs(function () {
expect(chatview.closeChat).toHaveBeenCalled(); expect(chatview.close).toHaveBeenCalled();
expect(converse.emit).toHaveBeenCalledWith('onChatBoxClosed', jasmine.any(Object)); expect(converse.emit).toHaveBeenCalledWith('onChatBoxClosed', jasmine.any(Object));
expect(converse.emit.callCount, 2); expect(converse.emit.callCount, 2);
}); });
}, converse)); }, converse));
it("can be toggled by clicking a DOM element with class 'toggle-chatbox-button'", function () { it("can be minimized by clicking a DOM element with class 'toggle-chatbox-button'", function () {
var chatbox = utils.openChatBoxes(1)[0], var chatbox = utils.openChatBoxes(1)[0],
chatview = this.chatboxviews.get(chatbox.get('jid')); chatview = this.chatboxviews.get(chatbox.get('jid')),
spyOn(chatview, 'toggleChatBox').andCallThrough(); trimmed_chatboxes = this.chatboxviews.trimmed_chatboxes_view;
spyOn(chatview, 'maximize').andCallThrough();
spyOn(chatview, 'minimize').andCallThrough();
spyOn(converse, 'emit'); spyOn(converse, 'emit');
spyOn(trimmed_chatboxes, 'onChanged').andCallThrough();
// We need to rebind all events otherwise our spy won't be called // We need to rebind all events otherwise our spy won't be called
chatview.delegateEvents(); chatview.delegateEvents();
runs(function () { runs(function () {
chatview.$el.find('.toggle-chatbox-button').click(); chatview.$el.find('.toggle-chatbox-button').click();
}); });
waits(250); waits(50);
runs(function () { runs(function () {
expect(chatview.toggleChatBox).toHaveBeenCalled(); expect(chatview.minimize).toHaveBeenCalled();
expect(converse.emit).toHaveBeenCalledWith('onChatBoxToggled', jasmine.any(Object)); expect(converse.emit).toHaveBeenCalledWith('onChatBoxMinimized', jasmine.any(Object));
expect(converse.emit.callCount, 2); expect(converse.emit.callCount, 2);
expect(chatview.$el.find('.chat-body').is(':visible')).toBeFalsy(); expect(chatview.$el.is(':visible')).toBeFalsy();
expect(chatview.$el.find('.toggle-chatbox-button').hasClass('icon-minus')).toBeFalsy();
expect(chatview.$el.find('.toggle-chatbox-button').hasClass('icon-plus')).toBeTruthy();
expect(chatview.model.get('minimized')).toBeTruthy(); expect(chatview.model.get('minimized')).toBeTruthy();
chatview.$el.find('.toggle-chatbox-button').click(); chatview.$el.find('.toggle-chatbox-button').click();
trimmedview = trimmed_chatboxes.get(chatview.model.get('id'));
trimmedview.$("a.restore-chat").click();
}); });
waits(250); waits(50);
runs(function () { runs(function () {
expect(chatview.toggleChatBox).toHaveBeenCalled(); expect(trimmed_chatboxes.onChanged).toHaveBeenCalled();
expect(converse.emit).toHaveBeenCalledWith('onChatBoxToggled', jasmine.any(Object)); expect(chatview.maximize).toHaveBeenCalled();
expect(converse.emit).toHaveBeenCalledWith('onChatBoxMaximized', jasmine.any(Object));
expect(chatview.$el.find('.chat-body').is(':visible')).toBeTruthy(); expect(chatview.$el.find('.chat-body').is(':visible')).toBeTruthy();
expect(chatview.$el.find('.toggle-chatbox-button').hasClass('icon-minus')).toBeTruthy(); expect(chatview.$el.find('.toggle-chatbox-button').hasClass('icon-minus')).toBeTruthy();
expect(chatview.$el.find('.toggle-chatbox-button').hasClass('icon-plus')).toBeFalsy(); expect(chatview.$el.find('.toggle-chatbox-button').hasClass('icon-plus')).toBeFalsy();
expect(chatview.model.get('minimized')).toBeFalsy(); expect(chatview.model.get('minimized')).toBeFalsy();
expect(converse.emit.callCount, 3);
}); });
}.bind(converse)); }.bind(converse));
it("will be removed from localStorage when closed", $.proxy(function () { it("will be removed from localStorage when closed", $.proxy(function () {
spyOn(converse, 'emit'); spyOn(converse, 'emit');
spyOn(converse.chatboxviews, 'trimOpenChats'); spyOn(converse.chatboxviews, 'trimChats');
this.chatboxes.localStorage._clear(); this.chatboxes.localStorage._clear();
runs(function () { runs(function () {
utils.closeControlBox(); utils.closeControlBox();
...@@ -176,7 +231,7 @@ ...@@ -176,7 +231,7 @@
expect(converse.emit).toHaveBeenCalledWith('onChatBoxClosed', jasmine.any(Object)); expect(converse.emit).toHaveBeenCalledWith('onChatBoxClosed', jasmine.any(Object));
expect(converse.chatboxes.length).toEqual(0); expect(converse.chatboxes.length).toEqual(0);
utils.openChatBoxes(6); utils.openChatBoxes(6);
expect(converse.chatboxviews.trimOpenChats).toHaveBeenCalled(); expect(converse.chatboxviews.trimChats).toHaveBeenCalled();
expect(converse.chatboxes.length).toEqual(6); expect(converse.chatboxes.length).toEqual(6);
expect(converse.emit).toHaveBeenCalledWith('onChatBoxOpened', jasmine.any(Object)); expect(converse.emit).toHaveBeenCalledWith('onChatBoxOpened', jasmine.any(Object));
utils.closeAllChatBoxes(); utils.closeAllChatBoxes();
...@@ -303,7 +358,7 @@ ...@@ -303,7 +358,7 @@
$toolbar = view.$el.find('ul.chat-toolbar'); $toolbar = view.$el.find('ul.chat-toolbar');
callButton = $toolbar.find('.toggle-call'); callButton = $toolbar.find('.toggle-call');
expect(callButton.length).toBe(0); expect(callButton.length).toBe(0);
view.closeChat(); view.close();
// Now check that it's shown if enabled and that it emits // Now check that it's shown if enabled and that it emits
// onCallButtonClicked // onCallButtonClicked
converse.visible_toolbar_buttons.call = true; // enable the button converse.visible_toolbar_buttons.call = true; // enable the button
...@@ -328,7 +383,7 @@ ...@@ -328,7 +383,7 @@
$toolbar = view.$el.find('ul.chat-toolbar'); $toolbar = view.$el.find('ul.chat-toolbar');
clearButton = $toolbar.find('.toggle-clear'); clearButton = $toolbar.find('.toggle-clear');
expect(clearButton.length).toBe(0); expect(clearButton.length).toBe(0);
view.closeChat(); view.close();
// Now check that it's shown if enabled and that it calls // Now check that it's shown if enabled and that it calls
// clearMessages // clearMessages
converse.visible_toolbar_buttons.clear = true; // enable the button converse.visible_toolbar_buttons.clear = true; // enable the button
...@@ -409,13 +464,13 @@ ...@@ -409,13 +464,13 @@
runs(function () { runs(function () {
utils.openChatBoxFor(contact_jid); utils.openChatBoxFor(contact_jid);
}); });
waits(250); waits(50);
runs(function () { runs(function () {
var chatview = converse.chatboxviews.get(contact_jid); var chatview = converse.chatboxviews.get(contact_jid);
expect(chatview.model.get('minimized')).toBeFalsy(); expect(chatview.model.get('minimized')).toBeFalsy();
chatview.$el.find('.toggle-chatbox-button').click(); chatview.$el.find('.toggle-chatbox-button').click();
}); });
waits(250); waits(50);
runs($.proxy(function () { runs($.proxy(function () {
var chatview = this.chatboxviews.get(contact_jid); var chatview = this.chatboxviews.get(contact_jid);
expect(chatview.model.get('minimized')).toBeTruthy(); expect(chatview.model.get('minimized')).toBeTruthy();
...@@ -431,11 +486,12 @@ ...@@ -431,11 +486,12 @@
this.chatboxes.onMessage(msg); this.chatboxes.onMessage(msg);
expect(this.emit).toHaveBeenCalledWith('onMessage', msg); expect(this.emit).toHaveBeenCalledWith('onMessage', msg);
}, converse)); }, converse));
waits(250); waits(50);
runs($.proxy(function () { runs($.proxy(function () {
var chatview = this.chatboxviews.get(contact_jid); var trimmed_chatboxes = this.chatboxviews.trimmed_chatboxes_view;
var $count = chatview.$el.find('.chat-head-message-count'); var trimmedview = trimmed_chatboxes.get(contact_jid);
expect(chatview.model.get('minimized')).toBeTruthy(); var $count = trimmedview.$el.find('.chat-head-message-count');
expect(trimmedview.model.get('minimized')).toBeTruthy();
expect($count.is(':visible')).toBeTruthy(); expect($count.is(':visible')).toBeTruthy();
expect($count.data('count')).toBe(1); expect($count.data('count')).toBe(1);
expect($count.html()).toBe('1'); expect($count.html()).toBe('1');
...@@ -449,23 +505,25 @@ ...@@ -449,23 +505,25 @@
.c('active', {'xmlns': 'http://jabber.org/protocol/chatstates'}).tree() .c('active', {'xmlns': 'http://jabber.org/protocol/chatstates'}).tree()
); );
}, converse)); }, converse));
waits(100); waits(50);
runs($.proxy(function () { runs($.proxy(function () {
var chatview = this.chatboxviews.get(contact_jid); var trimmed_chatboxes = this.chatboxviews.trimmed_chatboxes_view;
var $count = chatview.$el.find('.chat-head-message-count'); var trimmedview = trimmed_chatboxes.get(contact_jid);
expect(chatview.model.get('minimized')).toBeTruthy(); var $count = trimmedview.$el.find('.chat-head-message-count');
expect(trimmedview.model.get('minimized')).toBeTruthy();
expect($count.is(':visible')).toBeTruthy(); expect($count.is(':visible')).toBeTruthy();
expect($count.data('count')).toBe(2); expect($count.data('count')).toBe(2);
expect($count.html()).toBe('2'); expect($count.html()).toBe('2');
chatview.$el.find('.toggle-chatbox-button').click(); trimmedview.$el.find('.restore-chat').click();
}, converse)); }, converse));
waits(250); waits(50);
runs($.proxy(function () { runs($.proxy(function () {
var chatview = this.chatboxviews.get(contact_jid); var trimmed_chatboxes = this.chatboxviews.trimmed_chatboxes_view;
var $count = chatview.$el.find('.chat-head-message-count'); var trimmedview = trimmed_chatboxes.get(contact_jid);
expect(chatview.model.get('minimized')).toBeFalsy(); var $count = trimmedview.$el.find('.chat-head-message-count');
expect(trimmedview.model.get('minimized')).toBeFalsy();
expect($count.is(':visible')).toBeFalsy(); expect($count.is(':visible')).toBeFalsy();
expect($count.data('count')).toBe(0); expect($count.data('count')).toBeFalsy();
expect($count.html()).toBe('0'); expect($count.html()).toBe('0');
}, converse)); }, converse));
}, converse)); }, converse));
......
...@@ -115,14 +115,14 @@ ...@@ -115,14 +115,14 @@
it("can be saved to, and retrieved from, localStorage", $.proxy(function () { it("can be saved to, and retrieved from, localStorage", $.proxy(function () {
// We instantiate a new ChatBoxes collection, which by default // We instantiate a new ChatBoxes collection, which by default
// will be empty. // will be empty.
spyOn(this.chatboxviews, 'trimOpenChats'); spyOn(this.chatboxviews, 'trimChats');
utils.openControlBox(); utils.openControlBox();
var newchatboxes = new this.ChatBoxes(); var newchatboxes = new this.ChatBoxes();
expect(newchatboxes.length).toEqual(0); expect(newchatboxes.length).toEqual(0);
// The chatboxes will then be fetched from localStorage inside the // The chatboxes will then be fetched from localStorage inside the
// onConnected method // onConnected method
newchatboxes.onConnected(); newchatboxes.onConnected();
expect(this.chatboxviews.trimOpenChats).toHaveBeenCalled(); expect(this.chatboxviews.trimChats).toHaveBeenCalled();
expect(newchatboxes.length).toEqual(2); // XXX: Includes controlbox, is this a bug? expect(newchatboxes.length).toEqual(2); // XXX: Includes controlbox, is this a bug?
// Check that the chatrooms retrieved from localStorage // Check that the chatrooms retrieved from localStorage
// have the same attributes values as the original ones. // have the same attributes values as the original ones.
...@@ -139,33 +139,34 @@ ...@@ -139,33 +139,34 @@
this.rosterview.render(); this.rosterview.render();
}, converse)); }, converse));
it("can be toggled by clicking a DOM element with class 'toggle-chatbox-button'", function () { it("can be minimized by clicking a DOM element with class 'toggle-chatbox-button'", function () {
var view = this.chatboxviews.get('lounge@muc.localhost'), var view = this.chatboxviews.get('lounge@muc.localhost'),
chatroom = view.model, $el; trimmed_chatboxes = this.chatboxviews.trimmed_chatboxes_view;
spyOn(view, 'toggleChatBox').andCallThrough();
spyOn(view, 'minimize').andCallThrough();
spyOn(view, 'maximize').andCallThrough();
spyOn(converse, 'emit'); spyOn(converse, 'emit');
view.delegateEvents(); // We need to rebind all events otherwise our spy won't be called view.delegateEvents(); // We need to rebind all events otherwise our spy won't be called
runs(function () { runs(function () {
view.$el.find('.toggle-chatbox-button').click(); view.$el.find('.toggle-chatbox-button').click();
}); });
waits(250); waits(50);
runs(function () { runs(function () {
expect(view.toggleChatBox).toHaveBeenCalled(); expect(view.minimize).toHaveBeenCalled();
expect(converse.emit).toHaveBeenCalledWith('onChatBoxToggled', jasmine.any(Object)); expect(converse.emit).toHaveBeenCalledWith('onChatBoxMinimized', jasmine.any(Object));
expect(converse.emit.callCount, 2); expect(converse.emit.callCount, 2);
expect(view.$el.find('.chat-body').is(':visible')).toBeFalsy(); expect(view.$el.is(':visible')).toBeFalsy();
expect(view.$el.find('.toggle-chatbox-button').hasClass('icon-minus')).toBeFalsy();
expect(view.$el.find('.toggle-chatbox-button').hasClass('icon-plus')).toBeTruthy();
expect(view.model.get('minimized')).toBeTruthy(); expect(view.model.get('minimized')).toBeTruthy();
view.$el.find('.toggle-chatbox-button').click(); expect(view.minimize).toHaveBeenCalled();
trimmedview = trimmed_chatboxes.get(view.model.get('id'));
trimmedview.$("a.restore-chat").click();
}); });
waits(250); waits(50);
runs(function () { runs(function () {
expect(view.toggleChatBox).toHaveBeenCalled(); expect(view.maximize).toHaveBeenCalled();
expect(converse.emit).toHaveBeenCalledWith('onChatBoxToggled', jasmine.any(Object)); expect(converse.emit).toHaveBeenCalledWith('onChatBoxMaximized', jasmine.any(Object));
expect(view.$el.find('.chat-body').is(':visible')).toBeTruthy(); expect(view.$el.is(':visible')).toBeTruthy();
expect(view.$el.find('.toggle-chatbox-button').hasClass('icon-minus')).toBeTruthy();
expect(view.$el.find('.toggle-chatbox-button').hasClass('icon-plus')).toBeFalsy();
expect(view.model.get('minimized')).toBeFalsy(); expect(view.model.get('minimized')).toBeFalsy();
expect(converse.emit.callCount, 3); expect(converse.emit.callCount, 3);
}); });
...@@ -174,16 +175,16 @@ ...@@ -174,16 +175,16 @@
it("can be closed again by clicking a DOM element with class 'close-chatbox-button'", $.proxy(function () { it("can be closed again by clicking a DOM element with class 'close-chatbox-button'", $.proxy(function () {
var view = this.chatboxviews.get('lounge@muc.localhost'), chatroom = view.model, $el; var view = this.chatboxviews.get('lounge@muc.localhost'), chatroom = view.model, $el;
spyOn(view, 'closeChat').andCallThrough(); spyOn(view, 'close').andCallThrough();
spyOn(converse, 'emit'); spyOn(converse, 'emit');
spyOn(converse.connection.muc, 'leave'); spyOn(converse.connection.muc, 'leave');
view.delegateEvents(); // We need to rebind all events otherwise our spy won't be called view.delegateEvents(); // We need to rebind all events otherwise our spy won't be called
runs(function () { runs(function () {
view.$el.find('.close-chatbox-button').click(); view.$el.find('.close-chatbox-button').click();
}); });
waits(250); waits(50);
runs(function () { runs(function () {
expect(view.closeChat).toHaveBeenCalled(); expect(view.close).toHaveBeenCalled();
expect(this.connection.muc.leave).toHaveBeenCalled(); expect(this.connection.muc.leave).toHaveBeenCalled();
expect(this.emit).toHaveBeenCalledWith('onChatBoxClosed', jasmine.any(Object)); expect(this.emit).toHaveBeenCalledWith('onChatBoxClosed', jasmine.any(Object));
}.bind(converse)); }.bind(converse));
...@@ -204,7 +205,7 @@ ...@@ -204,7 +205,7 @@
afterEach($.proxy(function () { afterEach($.proxy(function () {
var view = this.chatboxviews.get('problematic@muc.localhost'); var view = this.chatboxviews.get('problematic@muc.localhost');
view.closeChat(); view.close();
}, converse)); }, converse));
it("will show an error message if the room requires a password", $.proxy(function () { it("will show an error message if the room requires a password", $.proxy(function () {
......
...@@ -8,13 +8,10 @@ ...@@ -8,13 +8,10 @@
); );
} (this, function (mock, utils) { } (this, function (mock, utils) {
describe("The Control Box", $.proxy(function (mock, utils) { describe("The Control Box", $.proxy(function (mock, utils) {
beforeEach(function () { beforeEach(function () {
runs(function () { runs(function () {
utils.openControlBox(); utils.openControlBox();
}); });
waits(250);
runs(function () {});
}); });
it("can be opened by clicking a DOM element with class 'toggle-online-users'", $.proxy(function () { it("can be opened by clicking a DOM element with class 'toggle-online-users'", $.proxy(function () {
...@@ -501,8 +498,6 @@ ...@@ -501,8 +498,6 @@
runs(function () { runs(function () {
utils.openControlBox(); utils.openControlBox();
}); });
waits(250);
runs(function () {});
}, converse)); }, converse));
it("contains two tabs, 'Contacts' and 'ChatRooms'", $.proxy(function () { it("contains two tabs, 'Contacts' and 'ChatRooms'", $.proxy(function () {
...@@ -524,8 +519,6 @@ ...@@ -524,8 +519,6 @@
runs(function () { runs(function () {
utils.openControlBox(); utils.openControlBox();
}); });
waits(250);
runs(function () {});
}, converse)); }, converse));
it("is opened by clicking the 'Chatrooms' tab", $.proxy(function () { it("is opened by clicking the 'Chatrooms' tab", $.proxy(function () {
......
...@@ -3,6 +3,7 @@ define("converse-dependencies", [ ...@@ -3,6 +3,7 @@ define("converse-dependencies", [
"moment", "moment",
"locales", "locales",
"backbone.localStorage", "backbone.localStorage",
"backbone.overview",
"jquery.tinysort", "jquery.tinysort",
"jquery.browser", "jquery.browser",
"strophe", "strophe",
......
...@@ -2,6 +2,7 @@ define("converse-dependencies", [ ...@@ -2,6 +2,7 @@ define("converse-dependencies", [
"moment", "moment",
"locales", "locales",
"backbone.localStorage", "backbone.localStorage",
"backbone.overview",
"jquery.tinysort", "jquery.tinysort",
"jquery.browser", "jquery.browser",
"strophe", "strophe",
......
define("converse-templates", [ define("converse-templates", [
"tpl!src/templates/action", "tpl!src/templates/action",
"tpl!src/templates/message",
"tpl!src/templates/new_day",
"tpl!src/templates/info",
"tpl!src/templates/controlbox",
"tpl!src/templates/chatbox",
"tpl!src/templates/toolbar",
"tpl!src/templates/contacts_tab",
"tpl!src/templates/contacts_panel",
"tpl!src/templates/chatrooms_tab",
"tpl!src/templates/login_tab",
"tpl!src/templates/add_contact_dropdown", "tpl!src/templates/add_contact_dropdown",
"tpl!src/templates/add_contact_form", "tpl!src/templates/add_contact_form",
"tpl!src/templates/room_item", "tpl!src/templates/change_status_message",
"tpl!src/templates/room_description", "tpl!src/templates/chat_status",
"tpl!src/templates/room_panel",
"tpl!src/templates/chatroom",
"tpl!src/templates/chatarea", "tpl!src/templates/chatarea",
"tpl!src/templates/chatbox",
"tpl!src/templates/chatroom",
"tpl!src/templates/chatrooms_tab",
"tpl!src/templates/choose_status",
"tpl!src/templates/contacts",
"tpl!src/templates/contacts_panel",
"tpl!src/templates/contacts_tab",
"tpl!src/templates/controlbox",
"tpl!src/templates/controlbox_toggle",
"tpl!src/templates/field",
"tpl!src/templates/form_checkbox",
"tpl!src/templates/form_input", "tpl!src/templates/form_input",
"tpl!src/templates/select_option",
"tpl!src/templates/form_select", "tpl!src/templates/form_select",
"tpl!src/templates/form_checkbox", "tpl!src/templates/info",
"tpl!src/templates/field", "tpl!src/templates/login_panel",
"tpl!src/templates/login_tab",
"tpl!src/templates/message",
"tpl!src/templates/new_day",
"tpl!src/templates/occupant", "tpl!src/templates/occupant",
"tpl!src/templates/roster_item",
"tpl!src/templates/pending_contact", "tpl!src/templates/pending_contact",
"tpl!src/templates/pending_contacts",
"tpl!src/templates/requesting_contact", "tpl!src/templates/requesting_contact",
"tpl!src/templates/requesting_contacts", "tpl!src/templates/requesting_contacts",
"tpl!src/templates/pending_contacts", "tpl!src/templates/room_description",
"tpl!src/templates/contacts", "tpl!src/templates/room_item",
"tpl!src/templates/chat_status", "tpl!src/templates/room_panel",
"tpl!src/templates/change_status_message", "tpl!src/templates/roster_item",
"tpl!src/templates/choose_status", "tpl!src/templates/select_option",
"tpl!src/templates/status_option", "tpl!src/templates/status_option",
"tpl!src/templates/login_panel", "tpl!src/templates/toolbar",
"tpl!src/templates/controlbox_toggle" "tpl!src/templates/trimmed_chat",
"tpl!src/templates/trimmed_chats"
], function () { ], function () {
return { return {
action: arguments[0], action: arguments[0],
message: arguments[1], add_contact_dropdown: arguments[1],
new_day: arguments[2], add_contact_form: arguments[2],
info: arguments[3], change_status_message: arguments[3],
controlbox: arguments[4], chat_status: arguments[4],
chatbox: arguments[5], chatarea: arguments[5],
toolbar: arguments[6], chatbox: arguments[6],
contacts_tab: arguments[7], chatroom: arguments[7],
contacts_panel: arguments[8], chatrooms_tab: arguments[8],
chatrooms_tab: arguments[9], choose_status: arguments[9],
login_tab: arguments[10], contacts: arguments[10],
add_contact_dropdown: arguments[11], contacts_panel: arguments[11],
add_contact_form: arguments[12], contacts_tab: arguments[12],
room_item: arguments[13], controlbox: arguments[13],
room_description: arguments[14], controlbox_toggle: arguments[14],
room_panel: arguments[15], field: arguments[15],
chatroom: arguments[16], form_checkbox: arguments[16],
chatarea: arguments[17], form_input: arguments[17],
form_input: arguments[18], form_select: arguments[18],
select_option: arguments[19], info: arguments[19],
form_select: arguments[20], login_panel: arguments[20],
form_checkbox: arguments[21], login_tab: arguments[21],
field: arguments[22], message: arguments[22],
occupant: arguments[23], new_day: arguments[23],
roster_item: arguments[24], occupant: arguments[24],
pending_contact: arguments[25], pending_contact: arguments[25],
requesting_contact: arguments[26], pending_contacts: arguments[26],
requesting_contacts: arguments[27], requesting_contact: arguments[27],
pending_contacts: arguments[28], requesting_contacts: arguments[28],
contacts: arguments[29], room_description: arguments[29],
chat_status: arguments[30], room_item: arguments[30],
change_status_message: arguments[31], room_panel: arguments[31],
choose_status: arguments[32], roster_item: arguments[32],
status_option: arguments[33], select_option: arguments[33],
login_panel: arguments[34], status_option: arguments[34],
controlbox_toggle: arguments[35] toolbar: arguments[35],
trimmed_chat: arguments[36],
trimmed_chats: arguments[37]
}; };
}); });
<div class="box-flyout {[if (minimized) {]} minimized {[}]}" <div class="box-flyout" style="height: {{height}}px">
{[if (!minimized) {]} style="height: {{height}}px" {[}]}> <div class="dragresize dragresize-tm"></div>
<div class="dragresize dragresize-tm" {[ if (minimized) { ]} style="display:none" {[ } ]}></div>
<div class="chat-head chat-head-chatbox"> <div class="chat-head chat-head-chatbox">
<div class="chat-head-message-count">0</div>
<a class="close-chatbox-button icon-close"></a> <a class="close-chatbox-button icon-close"></a>
<a class="toggle-chatbox-button <a class="toggle-chatbox-button icon-minus"></a>
{[ if (minimized) { ]} icon-plus {[ } ]}
{[ if (!minimized) { ]} icon-minus {[ } ]}
"></a>
<div class="chat-title"> <div class="chat-title">
{[ if (url) { ]} {[ if (url) { ]}
<a href="{{url}}" target="_blank" class="user"> <a href="{{url}}" target="_blank" class="user">
...@@ -19,7 +14,7 @@ ...@@ -19,7 +14,7 @@
</div> </div>
<p class="user-custom-message"><p/> <p class="user-custom-message"><p/>
</div> </div>
<div class="chat-body" {[ if (minimized) { ]} style="display:none" {[ } ]}> <div class="chat-body">
<div class="chat-content"></div> <div class="chat-content"></div>
<form class="sendXMPPMessage" action="" method="post"> <form class="sendXMPPMessage" action="" method="post">
{[ if (show_toolbar) { ]} {[ if (show_toolbar) { ]}
......
<div class="box-flyout {[if (minimized) {]} minimized {[}]}" <div class="box-flyout" style="height: {{height}}px"
{[if (!minimized) {]} style="height: {{height}}px" {[}]}> {[ if (minimized) { ]} style="display:none" {[ } ]}>
<div class="dragresize dragresize-tm" {[ if (minimized) { ]} style="display:none" {[ } ]}></div> <div class="dragresize dragresize-tm"></div>
<div class="chat-head chat-head-chatroom"> <div class="chat-head chat-head-chatroom">
<div class="chat-head-message-count">0</div> <div class="chat-head-message-count">0</div>
<a class="close-chatbox-button icon-close"></a> <a class="close-chatbox-button icon-close"></a>
<a class="toggle-chatbox-button <a class="toggle-chatbox-button icon-minus"></a>
{[ if (minimized) { ]} icon-plus {[ } ]}
{[ if (!minimized) { ]} icon-minus {[ } ]}
"></a>
<a class="configure-chatroom-button icon-wrench" style="display:none"></a> <a class="configure-chatroom-button icon-wrench" style="display:none"></a>
<div class="chat-title"> {{ name }} </div> <div class="chat-title"> {{ name }} </div>
<p class="chatroom-topic"><p/> <p class="chatroom-topic"><p/>
</div> </div>
<div class="chat-body" {[ if (minimized) { ]} style="display:none" {[ } ]}> <div class="chat-body"><span class="spinner centered"/></div>
<span class="spinner centered"/>
</div>
</div> </div>
<a class="close-chatbox-button icon-close"></a>
<div class="chat-title">
<a href="#" class="restore-chat">
<div class="chat-head-message-count">0</div>
{{ title }}
</a>
</div>
<div id="trimmed-chatboxes"><div class="box-flyout"></div></div>
...@@ -6,8 +6,10 @@ ...@@ -6,8 +6,10 @@
<meta name="description" content="Converse.js: A chat client for your website" /> <meta name="description" content="Converse.js: A chat client for your website" />
<link rel="shortcut icon" type="image/png" href="components/jasmine/images/jasmine_favicon.png"> <link rel="shortcut icon" type="image/png" href="components/jasmine/images/jasmine_favicon.png">
<link rel="stylesheet" type="text/css" href="components/jasmine/src/html/jasmine.css"> <link rel="stylesheet" type="text/css" href="components/jasmine/src/html/jasmine.css">
<link rel="stylesheet" type="text/css" media="screen" href="stylesheets/stylesheet.css"> <link type="text/css" rel="stylesheet" media="screen" href="http://netdna.bootstrapcdn.com/bootstrap/3.0.3/css/bootstrap.min.css" />
<link rel="stylesheet" type="text/css" media="screen" href="converse.css"> <link type="text/css" rel="stylesheet" media="screen" href="http://netdna.bootstrapcdn.com/font-awesome/4.0.3/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.css" />
<script src="main.js"></script> <script src="main.js"></script>
<script data-main="tests/main" src="components/requirejs/require.js"></script> <script data-main="tests/main" src="components/requirejs/require.js"></script>
</head> </head>
...@@ -19,6 +21,5 @@ ...@@ -19,6 +21,5 @@
<h2 id="project_tagline">Tests</h2> <h2 id="project_tagline">Tests</h2>
</header> </header>
</div> </div>
<div id="conversejs"></div>
</body> </body>
</html> </html>
...@@ -45,7 +45,7 @@ require([ ...@@ -45,7 +45,7 @@ require([
auto_subscribe: false, auto_subscribe: false,
animate: false, animate: false,
connection: mock.mock_connection, connection: mock.mock_connection,
testing: true no_trimming: true
}, function (converse) { }, function (converse) {
window.converse = converse; window.converse = converse;
window.crypto = { window.crypto = {
......
...@@ -13,7 +13,7 @@ ...@@ -13,7 +13,7 @@
var i, chatbox; var i, chatbox;
for (i=converse.chatboxes.models.length-1; i>-1; i--) { for (i=converse.chatboxes.models.length-1; i>-1; i--) {
chatbox = converse.chatboxes.models[i]; chatbox = converse.chatboxes.models[i];
converse.chatboxviews.get(chatbox.get('id')).closeChat(); converse.chatboxviews.get(chatbox.get('id')).close();
} }
return this; return this;
}; };
...@@ -22,17 +22,16 @@ ...@@ -22,17 +22,16 @@
var i, chatbox, num_chatboxes = converse.chatboxes.models.length; var i, chatbox, num_chatboxes = converse.chatboxes.models.length;
for (i=num_chatboxes-1; i>-1; i--) { for (i=num_chatboxes-1; i>-1; i--) {
chatbox = converse.chatboxes.models[i]; chatbox = converse.chatboxes.models[i];
converse.chatboxviews.get(chatbox.get('id')).closeChat(); converse.chatboxviews.get(chatbox.get('id')).close();
converse.chatboxviews.get(chatbox.get('id')).$el.remove(); converse.chatboxviews.get(chatbox.get('id')).$el.remove();
} }
converse.chatboxviews.get('controlbox').closeChat(); converse.chatboxviews.get('controlbox').close();
converse.chatboxviews.get('controlbox').$el.remove(); converse.chatboxviews.get('controlbox').$el.remove();
return this; return this;
}; };
utils.initConverse = function () { utils.initConverse = function () {
converse.chatboxes = new converse.ChatBoxes(); converse._initialize();
converse.chatboxviews = new converse.ChatBoxViews({model: converse.chatboxes});
converse.onConnected(); converse.onConnected();
}; };
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment