Merge branch 'templates' into 0.8

Conflicts:
	converse.js
This commit is contained in:
JC Brand 2014-01-22 19:29:47 +02:00
commit 823f93f006
46 changed files with 580 additions and 514 deletions

View File

@ -3,7 +3,9 @@
"version": "0.7.2",
"devDependencies": {
"jasmine": "https://github.com/jcbrand/jasmine.git#1_3_x",
"otr": "0.2.7"
"otr": "0.2.7",
"requirejs-text": "~2.0.10",
"requirejs-tpl-jfparadis": "*"
},
"dependencies": {
"requirejs": "2.1.8",
@ -20,7 +22,9 @@
"strophe.muc": "https://raw.github.com/strophe/strophejs-plugins/02310ad1b8da2962cd05b0f4bceaecca134efed4/muc/strophe.muc.js",
"otr": "0.2.7",
"crypto-js": "~3.1.2",
"almond": "~0.2.6"
"almond": "~0.2.6",
"requirejs-text": "~2.0.10",
"requirejs-tpl-jfparadis": "*"
},
"exportsOverride": {}
}

View File

@ -12,27 +12,26 @@
console = { log: function () {}, error: function () {} };
}
if (typeof define === 'function' && define.amd) {
define("converse", ["converse-dependencies"], function(otr) {
// Use Mustache style syntax for variable interpolation
_.templateSettings = {
evaluate : /\{\[([\s\S]+?)\]\}/g,
interpolate : /\{\{([\s\S]+?)\}\}/g
};
if (typeof otr !== "undefined") {
return factory(jQuery, _, otr.OTR, otr.DSA, console);
} else {
return factory(jQuery, _, undefined, undefined, console);
define("converse",
["converse-dependencies", "converse-templates"],
function(otr, templates) {
if (typeof otr !== "undefined") {
return factory(jQuery, _, otr.OTR, otr.DSA, console, templates);
} else {
return factory(jQuery, _, undefined, undefined, console, templates);
}
}
});
);
} else {
// Browser globals
// FIXME
_.templateSettings = {
evaluate : /\{\[([\s\S]+?)\]\}/g,
interpolate : /\{\{([\s\S]+?)\}\}/g
};
root.converse = factory(jQuery, _, OTR, DSA, console || {log: function(){}});
}
}(this, function ($, _, OTR, DSA, console) {
}(this, function ($, _, OTR, DSA, console, templates) {
$.fn.addHyperlinks = function() {
if (this.length > 0) {
this.each(function(i, obj) {
@ -86,6 +85,7 @@
};
var converse = {
templates: templates,
emit: function(evt, data) {
$(this).trigger(evt, data);
},
@ -731,99 +731,6 @@
'click .toggle-call': 'toggleCall'
},
template: _.template(
'<div class="chat-head chat-head-chatbox">' +
'<a class="close-chatbox-button icon-close"></a>' +
'<a href="{{url}}" target="_blank" class="user">' +
'<div class="chat-title"> {{ fullname }} </div>' +
'</a>' +
'<p class="user-custom-message"><p/>' +
'</div>' +
'<div class="chat-content"></div>' +
'<form class="sendXMPPMessage" action="" method="post">' +
'{[ if ('+converse.show_toolbar+') { ]}' +
'<ul class="chat-toolbar no-text-select"></ul>'+
'{[ } ]}' +
'<textarea ' +
'type="text" ' +
'class="chat-textarea" ' +
'placeholder="'+__('Personal message')+'"/>'+
'</form>'
),
toolbar_template: _.template(
'{[ if (show_emoticons) { ]}' +
'<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>' +
'{[ } ]}' +
'{[ if (' + converse.show_call_button + ') { ]}' +
'<li><a class="toggle-call icon-phone" title="Start a call"></a></li>' +
'{[ } ]}' +
'{[ if (allow_otr) { ]}' +
'<li class="toggle-otr {{otr_status_class}}" title="{{otr_tooltip}}">'+
'<span class="chat-toolbar-text">{{otr_translated_status}}</span>'+
'{[ if (otr_status == "'+UNENCRYPTED+'") { ]}' +
'<span class="icon-unlocked"></span>'+
'{[ } ]}' +
'{[ if (otr_status == "'+UNVERIFIED+'") { ]}' +
'<span class="icon-lock"></span>'+
'{[ } ]}' +
'{[ if (otr_status == "'+VERIFIED+'") { ]}' +
'<span class="icon-lock"></span>'+
'{[ } ]}' +
'{[ if (otr_status == "'+FINISHED+'") { ]}' +
'<span class="icon-unlocked"></span>'+
'{[ } ]}' +
'<ul>'+
'{[ if (otr_status == "'+UNENCRYPTED+'") { ]}' +
'<li><a class="start-otr" href="#">'+__('Start encrypted conversation')+'</a></li>'+
'{[ } ]}' +
'{[ if (otr_status != "'+UNENCRYPTED+'") { ]}' +
'<li><a class="start-otr" href="#">'+__('Refresh encrypted conversation')+'</a></li>'+
'<li><a class="end-otr" href="#">'+__('End encrypted conversation')+'</a></li>'+
'<li><a class="auth-otr" data-scheme="smp" href="#">'+__('Verify with SMP')+'</a></li>'+
'{[ } ]}' +
'{[ if (otr_status == "'+UNVERIFIED+'") { ]}' +
'<li><a class="auth-otr" data-scheme="fingerprint" href="#">'+__('Verify with fingerprints')+'</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>'+
'{[ } ]}'
),
message_template: _.template(
'<div class="chat-message {{extra_classes}}">' +
'<span class="chat-message-{{sender}}">{{time}} {{username}}:&nbsp;</span>' +
'<span class="chat-message-content">{{message}}</span>' +
'</div>'),
action_template: _.template(
'<div class="chat-message {{extra_classes}}">' +
'<span class="chat-message-{{sender}}">{{time}} **{{username}} </span>' +
'<span class="chat-message-content">{{message}}</span>' +
'</div>'),
new_day_template: _.template(
'<time class="chat-date" datetime="{{isodate}}">{{datestring}}</time>'
),
initialize: function (){
this.model.messages.on('add', this.onMessageAdded, this);
this.model.on('show', this.show, this);
@ -849,7 +756,17 @@
render: function () {
this.$el.attr('id', this.model.get('box_id'))
.html(this.template(this.model.toJSON()));
.html(
converse.templates.chatbox(
_.extend(
this.model.toJSON(),
{
show_toolbar: converse.show_toolbar,
label_personal_message: __('Personal message')
}
)
)
);
this.renderToolbar().renderAvatar();
return this;
},
@ -861,35 +778,6 @@
this.scrollDown();
},
renderEmoticons: function (text) {
if (converse.show_emoticons) {
text = text.replace(/:\)/g, '<span class="emoticon icon-smiley"></span>');
text = text.replace(/:\-\)/g, '<span class="emoticon icon-smiley"></span>');
text = text.replace(/;\)/g, '<span class="emoticon icon-wink"></span>');
text = text.replace(/;\-\)/g, '<span class="emoticon icon-wink"></span>');
text = text.replace(/:D/g, '<span class="emoticon icon-grin"></span>');
text = text.replace(/:\-D/g, '<span class="emoticon icon-grin"></span>');
text = text.replace(/:P/g, '<span class="emoticon icon-tongue"></span>');
text = text.replace(/:\-P/g, '<span class="emoticon icon-tongue"></span>');
text = text.replace(/:p/g, '<span class="emoticon icon-tongue"></span>');
text = text.replace(/:\-p/g, '<span class="emoticon icon-tongue"></span>');
text = text.replace(/8\)/g, '<span class="emoticon icon-cool"></span>');
text = text.replace(/>:\)/g, '<span class="emoticon icon-evil"></span>');
text = text.replace(/:S/g, '<span class="emoticon icon-confused"></span>');
text = text.replace(/:\\/g, '<span class="emoticon icon-wondering"></span>');
text = text.replace(/:\//g, '<span class="emoticon icon-wondering"></span>');
text = text.replace(/>:\(/g, '<span class="emoticon icon-angry"></span>');
text = text.replace(/:\(/g, '<span class="emoticon icon-sad"></span>');
text = text.replace(/:\-\(/g, '<span class="emoticon icon-sad"></span>');
text = text.replace(/:O/g, '<span class="emoticon icon-shocked"></span>');
text = text.replace(/:\-O/g, '<span class="emoticon icon-shocked"></span>');
text = text.replace(/\=\-O/g, '<span class="emoticon icon-shocked"></span>');
text = text.replace(/\(\^.\^\)b/g, '<span class="emoticon icon-thumbs-up"></span>');
text = text.replace(/<3/g, '<span class="emoticon icon-heart"></span>');
}
return text;
},
showMessage: function ($el, msg_dict) {
var this_date = converse.parseISO8601(msg_dict.time),
text = msg_dict.message,
@ -899,10 +787,10 @@
if ((match) && (match[1] === 'me')) {
text = text.replace(/^\/me/, '');
template = this.action_template;
template = converse.templates.action_template;
username = msg_dict.fullname;
} else {
template = this.message_template;
template = converse.templates.message;
username = sender === 'me' && __('me') || msg_dict.fullname;
}
$el.find('div.chat-event').remove();
@ -925,7 +813,7 @@
var $el = this.$el.find('.chat-content');
$el.find('div.chat-event').remove();
$el.append(
this.message_template({
converse.templates.message({
'sender': sender,
'time': (new Date()).toTimeString().substring(0,5),
'message': text,
@ -961,7 +849,7 @@
isodate.setUTCHours(0,0,0,0);
isodate = converse.toISOString(isodate);
if (this.isDifferentDay(prev_date, this_date)) {
$chat_content.append(this.new_day_template({
$chat_content.append(converse.templates.new_day({
isodate: isodate,
datestring: this_date.toString().substring(0,15)
}));
@ -1268,11 +1156,27 @@
} else if (data.otr_status == FINISHED){
data.otr_tooltip = __('Your buddy has closed their end of the private session, you should do the same');
}
data.allow_otr = converse.allow_otr && !this.is_chatroom;
data.show_emoticons = converse.show_emoticons;
data.otr_translated_status = OTR_TRANSLATED_MAPPING[data.otr_status];
data.otr_status_class = OTR_CLASS_MAPPING[data.otr_status];
this.$el.find('.chat-toolbar').html(this.toolbar_template(data));
this.$el.find('.chat-toolbar').html(
converse.templates.toolbar(
_.extend(data, {
FINISHED: FINISHED,
UNENCRYPTED: UNENCRYPTED,
UNVERIFIED: UNVERIFIED,
VERIFIED: VERIFIED,
allow_otr: converse.allow_otr && !this.is_chatroom,
label_end_encrypted_conversation: __('End encrypted conversation'),
label_refresh_encrypted_conversation: __('Refresh encrypted conversation'),
label_start_encrypted_conversation: __('Start encrypted conversation'),
label_verify_with_fingerprints: __('Verify with fingerprints'),
label_verify_with_smp: __('Verify with SMP'),
label_whats_this: __("What\'s this?"),
otr_status_class: OTR_CLASS_MAPPING[data.otr_status],
otr_translated_status: OTR_TRANSLATED_MAPPING[data.otr_status],
show_call_button: converse.show_call_button,
show_emoticons: converse.show_emoticons
})
)
);
}
return this;
},
@ -1351,49 +1255,6 @@
'click a.subscribe-to-user': 'addContactFromList'
},
tab_template: _.template('<li><a class="s current" href="#users">'+__('Contacts')+'</a></li>'),
template: _.template(
'<form class="set-xmpp-status" action="" method="post">'+
'<span id="xmpp-status-holder">'+
'<select id="select-xmpp-status" style="display:none">'+
'<option value="online">'+__('Online')+'</option>'+
'<option value="dnd">'+__('Busy')+'</option>'+
'<option value="away">'+__('Away')+'</option>'+
'<option value="offline">'+__('Offline')+'</option>'+
'</select>'+
'</span>'+
'</form>'
),
add_contact_dropdown_template: _.template(
'<dl class="add-converse-contact dropdown">' +
'<dt id="xmpp-contact-search" class="fancy-dropdown">' +
'<a class="toggle-xmpp-contact-form" href="#"'+
'title="'+__('Click to add new chat contacts')+'">'+
'<span class="icon-plus"></span>'+__('Add a contact')+'</a>' +
'</dt>' +
'<dd class="search-xmpp" style="display:none"><ul></ul></dd>' +
'</dl>'
),
add_contact_form_template: _.template(
'<li>'+
'<form class="add-xmpp-contact">' +
'<input type="text" name="identifier" class="username" placeholder="'+__('Contact username')+'"/>' +
'<button type="submit">'+__('Add')+'</button>' +
'</form>'+
'<li>'
),
search_contact_template: _.template(
'<li>'+
'<form class="search-xmpp-contact">' +
'<input type="text" name="identifier" class="username" placeholder="'+__('Contact name')+'"/>' +
'<button type="submit">'+__('Search')+'</button>' +
'</form>'+
'<li>'
),
initialize: function (cfg) {
cfg.$parent.append(this.$el);
this.$tabs = cfg.$parent.parent().find('#controlbox-tabs');
@ -1401,17 +1262,30 @@
render: function () {
var markup;
var widgets = this.template();
this.$tabs.append(this.tab_template());
var widgets = converse.templates.contacts_panel({
label_online: __('Online'),
label_busy: __('Busy'),
label_away: __('Away'),
label_offline: __('Offline')
});
this.$tabs.append(converse.templates.contacts_tab({label_contacts: __('Contacts')}));
if (converse.xhr_user_search) {
markup = this.search_contact_template();
markup = converse.templates.search_contact({
label_contact_name: __('Contact name'),
label_search: __('Search')
});
} else {
markup = this.add_contact_form_template();
}
markup = converse.templates.add_contact_form({
label_contact_username: __('Contact username'),
label_add: __('Add')
});
}
if (converse.allow_contact_requests) {
widgets += this.add_contact_dropdown_template();
widgets += converse.templates.add_contact_dropdown({
label_click_to_chat: __('Click to add new chat contacts'),
label_add_contact: __('Add a contact')
});
}
this.$el.html(widgets);
@ -1492,74 +1366,17 @@
'click a.open-room': 'createChatRoom',
'click a.room-info': 'showRoomInfo'
},
room_template: _.template(
'<dd class="available-chatroom">'+
'<a class="open-room" data-room-jid="{{jid}}"'+
'title="'+__('Click to open this room')+'" href="#">{{name}}</a>'+
'<a class="room-info icon-room-info" data-room-jid="{{jid}}"'+
'title="'+__('Show more information on this room')+'" href="#">&nbsp;</a>'+
'</dd>'),
// FIXME: check markup in mockup
room_description_template: _.template(
'<div class="room-info">'+
'<p class="room-info"><strong>'+__('Description:')+'</strong> {{desc}}</p>' +
'<p class="room-info"><strong>'+__('Occupants:')+'</strong> {{occ}}</p>' +
'<p class="room-info"><strong>'+__('Features:')+'</strong> <ul>'+
'{[ if (passwordprotected) { ]}' +
'<li class="room-info locked">'+__('Requires authentication')+'</li>' +
'{[ } ]}' +
'{[ if (hidden) { ]}' +
'<li class="room-info">'+__('Hidden')+'</li>' +
'{[ } ]}' +
'{[ if (membersonly) { ]}' +
'<li class="room-info">'+__('Requires an invitation')+'</li>' +
'{[ } ]}' +
'{[ if (moderated) { ]}' +
'<li class="room-info">'+__('Moderated')+'</li>' +
'{[ } ]}' +
'{[ if (nonanonymous) { ]}' +
'<li class="room-info">'+__('Non-anonymous')+'</li>' +
'{[ } ]}' +
'{[ if (open) { ]}' +
'<li class="room-info">'+__('Open room')+'</li>' +
'{[ } ]}' +
'{[ if (persistent) { ]}' +
'<li class="room-info">'+__('Permanent room')+'</li>' +
'{[ } ]}' +
'{[ if (publicroom) { ]}' +
'<li class="room-info">'+__('Public')+'</li>' +
'{[ } ]}' +
'{[ if (semianonymous) { ]}' +
'<li class="room-info">'+__('Semi-anonymous')+'</li>' +
'{[ } ]}' +
'{[ if (temporary) { ]}' +
'<li class="room-info">'+__('Temporary room')+'</li>' +
'{[ } ]}' +
'{[ if (unmoderated) { ]}' +
'<li class="room-info">'+__('Unmoderated')+'</li>' +
'{[ } ]}' +
'</p>' +
'</div>'
),
tab_template: _.template('<li><a class="s" href="#chatrooms">'+__('Rooms')+'</a></li>'),
template: _.template(
'<form class="add-chatroom" action="" method="post">'+
'<input type="text" name="chatroom" class="new-chatroom-name" placeholder="'+__('Room name')+'"/>'+
'<input type="text" name="nick" class="new-chatroom-nick" placeholder="'+__('Nickname')+'"/>'+
'<input type="{{ server_input_type }}" name="server" class="new-chatroom-server" placeholder="'+__('Server')+'"/>'+
'<input type="submit" name="join" value="'+__('Join')+'"/>'+
'<input type="button" name="show" id="show-rooms" value="'+__('Show rooms')+'"/>'+
'</form>'+
'<dl id="available-chatrooms"></dl>'),
initialize: function (cfg) {
cfg.$parent.append(
this.$el.html(
this.template({
server_input_type: converse.hide_muc_server && 'hidden' || 'text'
converse.templates.room_panel({
'server_input_type': converse.hide_muc_server && 'hidden' || 'text',
'label_room_name': __('Room name'),
'label_nickname': __('Nickname'),
'label_server': __('Server'),
'label_join': __('Join'),
'label_show_rooms': __('Show rooms')
})
).hide());
this.$tabs = cfg.$parent.parent().find('#controlbox-tabs');
@ -1579,7 +1396,7 @@
},
render: function () {
this.$tabs.append(this.tab_template());
this.$tabs.append(converse.templates.chatrooms_tab({label_rooms: __('Rooms')}));
return this;
},
@ -1606,10 +1423,14 @@
for (i=0; i<this.rooms.length; i++) {
name = Strophe.unescapeNode($(this.rooms[i]).attr('name')||$(this.rooms[i]).attr('jid'));
jid = $(this.rooms[i]).attr('jid');
fragment.appendChild($(this.room_template({
'name':name,
'jid':jid
}))[0]);
fragment.appendChild($(
converse.templates.room_item({
'name':name,
'jid':jid,
'open_title': __('Click to open this room'),
'info_title': __('Show more information on this room')
})
)[0]);
}
$available_chatrooms.append(fragment);
$('input#show-rooms').show().siblings('span.spinner').remove();
@ -1655,7 +1476,7 @@
var $stanza = $(stanza);
// All MUC features found here: http://xmpp.org/registrar/disco-features.html
$dd.find('span.spinner').replaceWith(
this.room_description_template({
converse.templates.room_description({
'desc': $stanza.find('field[var="muc#roominfo_description"] value').text(),
'occ': $stanza.find('field[var="muc#roominfo_occupants"] value').text(),
'hidden': $stanza.find('feature[var="muc_hidden"]').length,
@ -1668,7 +1489,21 @@
'publicroom': $stanza.find('feature[var="muc_public"]').length,
'semianonymous': $stanza.find('feature[var="muc_semianonymous"]').length,
'temporary': $stanza.find('feature[var="muc_temporary"]').length,
'unmoderated': $stanza.find('feature[var="muc_unmoderated"]').length
'unmoderated': jstanza.find('feature[var="muc_unmoderated"]').length,
'label_desc': __('Description:'),
'label_occ': __('Occupants:'),
'label_features': __('Features:'),
'label_requires_auth': __('Requires authentication'),
'label_hidden': __('Hidden'),
'label_requires_invite': __('Requires an invitation'),
'label_moderated': __('Moderated'),
'label_non_anon': __('Non-anonymous'),
'label_open_room': __('Open room'),
'label_permanent_room': __('Permanent room'),
'label_public': __('Public'),
'label_semi_anon': _('Semi-anonymous'),
'label_temp_room': _('Temporary room'),
'label_unmoderated': __('Unmoderated')
}));
}, this));
}
@ -1770,14 +1605,6 @@
}
},
template: _.template(
'<div class="chat-head controlbox-head">'+
'<ul id="controlbox-tabs"></ul>'+
'<a class="close-chatbox-button icon-close"></a>'+
'</div>'+
'<div class="controlbox-panes"></div>'
),
switchTab: function (ev) {
ev.preventDefault();
var $tab = $(ev.target),
@ -1801,11 +1628,11 @@
render: function () {
if ((!converse.prebind) && (!converse.connection)) {
// Add login panel if the user still has to authenticate
this.$el.html(this.template(this.model.toJSON()));
this.$el.html(converse.templates.controlbox(this.model.toJSON()));
this.loginpanel = new converse.LoginPanel({'$parent': this.$el.find('.controlbox-panes'), 'model': this});
this.loginpanel.render();
} else if (!this.contactspanel) {
this.$el.html(this.template(this.model.toJSON()));
this.$el.html(converse.templates.controlbox(this.model.toJSON()));
this.contactspanel = new converse.ContactsPanel({'$parent': this.$el.find('.controlbox-panes')});
this.contactspanel.render();
converse.xmppstatusview = new converse.XMPPStatusView({'model': converse.xmppstatus});
@ -1830,7 +1657,6 @@
'click .toggle-smiley ul li': 'insertEmoticon',
'keypress textarea.chat-textarea': 'keyPressed'
},
info_template: _.template('<div class="chat-info">{{message}}</div>'),
is_chatroom: true,
sendChatRoomMessage: function (body) {
@ -1876,42 +1702,20 @@
}
},
template: _.template(
'<div class="chat-head chat-head-chatroom">' +
'<a class="close-chatbox-button icon-close"></a>' +
'<a class="configure-chatroom-button icon-wrench" style="display:none"></a>' +
'<div class="chat-title"> {{ name }} </div>' +
'<p class="chatroom-topic"><p/>' +
'</div>' +
'<div class="chat-body">' +
'<span class="spinner centered"/>' +
'</div>'),
chatarea_template: _.template(
'<div class="chat-area">' +
'<div class="chat-content"></div>' +
'<form class="sendXMPPMessage" action="" method="post">' +
'{[ if ('+converse.show_toolbar+') { ]}' +
'<ul class="chat-toolbar no-text-select"></ul>'+
'{[ } ]}' +
'<textarea type="text" class="chat-textarea" ' +
'placeholder="'+__('Message')+'"/>' +
'</form>' +
'</div>' +
'<div class="participants">' +
'<ul class="participant-list"></ul>' +
'</div>'
),
render: function () {
this.$el.attr('id', this.model.get('box_id'))
.html(this.template(this.model.toJSON()));
.html(converse.templates.chatroom(this.model.toJSON()));
return this;
},
renderChatArea: function () {
if (!this.$el.find('.chat-area').length) {
this.$el.find('.chat-body').empty().append(this.chatarea_template());
this.$el.find('.chat-body').empty().append(
converse.templates.chatarea({
'show_toolbar': converse.show_toolbar,
'label_message': __('Message')
})
);
this.renderToolbar();
}
return this;
@ -1954,11 +1758,6 @@
this.model.set('connected', false);
},
form_input_template: _.template('<label>{{label}}<input name="{{name}}" type="{{type}}" value="{{value}}"></label>'),
select_option_template: _.template('<option value="{{value}}">{{label}}</option>'),
form_select_template: _.template('<label>{{label}}<select name="{{name}}">{{options}}</select></label>'),
form_checkbox_template: _.template('<label>{{label}}<input name="{{name}}" type="{{type}}" {{checked}}"></label>'),
renderConfigurationForm: function (stanza) {
var $form= this.$el.find('form.chatroom-form'),
$stanza = $(stanza),
@ -1984,7 +1783,7 @@
options = [];
$options = $field.find('option');
for (j=0; j<$options.length; j++) {
options.push(this.select_option_template({
options.push(converse.templates.select_option({
value: $($options[j]).find('value').text(),
label: $($options[j]).attr('label')
}));
@ -1995,14 +1794,14 @@
options: options.join('')
}));
} else if ($field.attr('type') == 'boolean') {
$form.append(this.form_checkbox_template({
$form.append(converse.templates.form_checkbox({
name: $field.attr('var'),
type: input_types[$field.attr('type')],
label: $field.attr('label') || '',
checked: $field.find('value').text() === "1" && 'checked="1"' || ''
}));
} else {
$form.append(this.form_input_template({
$form.append(converse.templates.form_input({
name: $field.attr('var'),
type: input_types[$field.attr('type')],
label: $field.attr('label') || '',
@ -2016,8 +1815,6 @@
$form.find('input[type=button]').on('click', $.proxy(this.cancelConfiguration, this));
},
field_template: _.template('<field var="{{name}}"><value>{{value}}</value></field>'),
saveConfiguration: function (ev) {
ev.preventDefault();
var that = this;
@ -2031,7 +1828,7 @@
} else {
value = $input.val();
}
var cnode = $(that.field_template({
var cnode = $(converse.templates.field({
name: $input.attr('name'),
value: value
}))[0];
@ -2193,10 +1990,10 @@
}
this.renderChatArea();
for (i=0; i<info_msgs.length; i++) {
$chat_content.append(this.info_template({message: info_msgs[i]}));
$chat_content.append(converse.templates.info({message: info_msgs[i]}));
}
for (i=0; i<action_msgs.length; i++) {
$chat_content.append(this.info_template({message: action_msgs[i]}));
$chat_content.append(converse.templates.info({message: action_msgs[i]}));
}
return this.scrollDown();
},
@ -2294,7 +2091,7 @@
message_date.setUTCHours(0,0,0,0);
isodate = converse.toISOString(message_date);
if (_.indexOf(dates, isodate) == -1) {
$chat_content.append(this.new_day_template({
$chat_content.append(converse.templates.new_day({
isodate: isodate,
datestring: message_date.toString().substring(0,15)
}));
@ -2305,7 +2102,7 @@
// # For translators: the %1$s and %2$s parts will get replaced by the user and topic text respectively
// # Example: Topic set by JC Brand to: Hello World!
$chat_content.append(
this.info_template({
converse.templates.info({
'message': __('Topic set by %1$s to: %2$s', sender, subject)
}));
}
@ -2324,20 +2121,6 @@
return true;
},
occupant_template: _.template(
'<li class="{{role}}" '+
'{[ if (role === "moderator") { ]}' +
'title="'+__('This user is a moderator')+'"' +
'{[ } ]}'+
'{[ if (role === "participant") { ]}' +
'title="'+__('This user can send messages in this room')+'"' +
'{[ } ]}'+
'{[ if (role === "visitor") { ]}' +
'title="'+__('This user can NOT send messages in this room')+'"' +
'{[ } ]}'+
'>{{nick}}</li>'
),
onChatRoomRoster: function (roster, room) {
this.renderChatArea();
var controlboxview = converse.chatboxesview.views.controlbox,
@ -2347,9 +2130,12 @@
this.$el.find('.participant-list').empty();
for (i=0; i<roster_size; i++) {
participants.push(
this.occupant_template({
role: roster[keys[i]].role,
nick: Strophe.unescapeNode(keys[i])
converse.templates.occupant({
'role': roster[keys[i]].role,
'nick': Strophe.unescapeNode(keys[i]),
'desc_moderator': __('This user is a moderator'),
'desc_participant': __('This user can send messages in this room'),
'desc_visitor': __('This user can NOT send messages in this room')
}));
}
$participant_list.append(participants.join(""));
@ -2558,23 +2344,6 @@
this.model.destroy();
},
template: _.template(
'<a class="open-chat" title="'+__('Click to chat with this contact')+'" href="#">'+
'<span class="icon-{{ chat_status }}" title="{{ status_desc }}"></span>{{ fullname }}'+
'</a>' +
'<a class="remove-xmpp-contact icon-remove" title="'+__('Click to remove this contact')+'" href="#"></a>'),
pending_template: _.template(
'<span>{{ fullname }}</span>' +
'<a class="remove-xmpp-contact icon-remove" title="'+__('Click to remove this contact')+'" href="#"></a>'),
request_template: _.template('<div>{{ fullname }}</div>' +
'<button type="button" class="accept-xmpp-request">' +
'Accept</button>' +
'<button type="button" class="decline-xmpp-request">' +
'Decline</button>' +
''),
render: function () {
var item = this.model,
ask = item.get('ask'),
@ -2598,15 +2367,28 @@
if (ask === 'subscribe') {
this.$el.addClass('pending-xmpp-contact');
this.$el.html(this.pending_template(item.toJSON()));
this.$el.html(converse.templates.pending_contact(
_.extend(item.toJSON(), {
'desc_remove': __('Click to remove this contact')
})
));
} else if (requesting === true) {
this.$el.addClass('requesting-xmpp-contact');
this.$el.html(this.request_template(item.toJSON()));
this.$el.html(converse.templates.requesting_contact(
_.extend(item.toJSON(), {
'label_accept': __('Accept'),
'label_decline': __('Decline')
})
));
converse.controlboxtoggle.showControlBox();
} else if (subscription === 'both' || subscription === 'to') {
this.$el.addClass('current-xmpp-contact');
this.$el.html(this.template(
_.extend(item.toJSON(), {'status_desc': STATUSES[item.get('chat_status')||'offline']})
this.$el.html(converse.templates.roster_item(
_.extend(item.toJSON(), {
'desc_status': STATUSES[item.get('chat_status')||'offline'],
'desc_chat': __('Click to chat with this contact'),
'desc_remove': __('Click to remove this contact')
})
));
}
return this;
@ -2915,15 +2697,6 @@
id: 'converse-roster',
rosteritemviews: {},
requesting_contacts_template: _.template(
'<dt id="xmpp-contact-requests">'+__('Contact requests')+'</dt>'),
contacts_template: _.template(
'<dt id="xmpp-contacts">'+__('My contacts')+'</dt>'),
pending_contacts_template: _.template(
'<dt id="pending-xmpp-contacts">'+__('Pending contacts')+'</dt>'),
initialize: function () {
this.model.on("add", function (item) {
this.addRosterItemView(item).render(item);
@ -2944,9 +2717,18 @@
this.model.on("remove", function (item) { this.removeRosterItemView(item); }, this);
this.model.on("destroy", function (item) { this.removeRosterItemView(item); }, this);
var roster_markup = this.contacts_template();
var roster_markup = converse.templates.contacts({
'label_contacts': __('My contacts')
});
if (converse.allow_contact_requests) {
roster_markup = this.requesting_contacts_template() + roster_markup + this.pending_contacts_template();
roster_markup =
converse.templates.requesting_contacts({
'label_contact_requests': __('Contact requests')
}) +
roster_markup +
converse.templates.pending_contacts({
'label_pending_contacts': __('Pending contacts')
});
}
this.$el.hide().html(roster_markup);
@ -3161,26 +2943,14 @@
$(ev.target).parent().parent().siblings('dd').find('ul').toggle('fast');
},
change_status_message_template: _.template(
'<form id="set-custom-xmpp-status">' +
'<input type="text" class="custom-xmpp-status" {{ status_message }}"'+
'placeholder="'+__('Custom status')+'"/>' +
'<button type="submit">'+__('Save')+'</button>' +
'</form>'),
status_template: _.template(
'<div class="xmpp-status">' +
'<a class="choose-xmpp-status {{ chat_status }}" data-value="{{status_message}}" href="#" title="'+__('Click to change your chat status')+'">' +
'<span class="icon-{{ chat_status }}"></span>'+
'{{ status_message }}' +
'</a>' +
'<a class="change-xmpp-status-message icon-pencil" href="#" title="'+__('Click here to write a custom status message')+'"></a>' +
'</div>'),
renderStatusChangeForm: function (ev) {
ev.preventDefault();
var status_message = this.model.get('status') || 'offline';
var input = this.change_status_message_template({'status_message': status_message});
var input = converse.templates.change_status_message({
'status_message': status_message,
'label_custom_status': __('Custom status'),
'label_save': __('Save')
});
this.$el.find('.xmpp-status').replaceWith(input);
this.$el.find('.custom-xmpp-status').focus().focus();
},
@ -3225,26 +2995,14 @@
// # Example, I am online
var status_message = model.get('status_message') || __("I am %1$s", this.getPrettyStatus(stat));
this.$el.find('#fancy-xmpp-status-select').html(
this.status_template({
converse.templates.chat_status({
'chat_status': stat,
'status_message': status_message
'status_message': status_message,
'desc_custom_status': __('Click here to write a custom status message'),
'desc_change_status': __('Click to change your chat status')
}));
},
choose_template: _.template(
'<dl id="target" class="dropdown">' +
'<dt id="fancy-xmpp-status-select" class="fancy-dropdown"></dt>' +
'<dd><ul class="xmpp-status-menu"></ul></dd>' +
'</dl>'),
option_template: _.template(
'<li>' +
'<a href="#" class="{{ value }}" data-value="{{ value }}">'+
'<span class="icon-{{ value }}"></span>'+
'{{ text }}'+
'</a>' +
'</li>'),
initialize: function () {
this.model.on("change", this.updateStatusUI, this);
},
@ -3257,17 +3015,20 @@
$options_target,
options_list = [],
that = this;
this.$el.html(this.choose_template());
this.$el.html(converse.templates.choose_status());
this.$el.find('#fancy-xmpp-status-select')
.html(this.status_template({
.html(converse.templates.chat_status({
'status_message': this.model.get('status_message') || __("I am %1$s", this.getPrettyStatus(chat_status)),
'chat_status': chat_status
'chat_status': chat_status,
'desc_custom_status': __('Click here to write a custom status message'),
'desc_change_status': __('Click to change your chat status')
}));
// iterate through all the <option> elements and add option values
options.each(function(){
options_list.push(that.option_template({'value': $(this).val(),
'text': this.text
}));
options_list.push(converse.templates.status_option({
'value': $(this).val(),
'text': this.text
}));
});
$options_target = this.$el.find("#target dd ul").hide();
$options_target.append(options_list.join(''));
@ -3330,22 +3091,6 @@
events: {
'submit form#converse-login': 'authenticate'
},
tab_template: _.template(
'<li><a class="current" href="#login">'+__('Sign in')+'</a></li>'
),
template: _.template(
'<form id="converse-login">' +
'<label>'+__('XMPP/Jabber Username:')+'</label>' +
'<input type="username" name="jid">' +
'<label>'+__('Password:')+'</label>' +
'<input type="password" name="password">' +
'<input class="login-submit" type="submit" value="'+__('Log In')+'">' +
'</form">'
),
bosh_url_input: _.template(
'<label>'+__('BOSH Service URL:')+'</label>' +
'<input type="text" id="bosh_service_url">'
),
connect: function ($form, jid, password) {
if ($form) {
@ -3364,14 +3109,20 @@
},
initialize: function (cfg) {
cfg.$parent.html(this.$el.html(this.template()));
cfg.$parent.html(this.$el.html(
converse.templates.login_panel({
'label_username': __('XMPP/Jabber Username:'),
'label_password': __('Password:'),
'label_login': __('Log In')
})
));
this.$tabs = cfg.$parent.parent().find('#controlbox-tabs');
this.model.on('connection-fail', function () { this.showConnectButton(); }, this);
this.model.on('auth-fail', function () { this.showConnectButton(); }, this);
},
render: function () {
this.$tabs.append(this.tab_template());
this.$tabs.append(converse.templates.login_tab({label_sign_in: __('Sign in')}));
this.$el.find('input#jid').focus();
return this;
},
@ -3423,17 +3174,16 @@
'href': "#"
},
template: _.template(
'<strong class="conn-feedback">Toggle chat</strong>'+
'<strong style="display: none" id="online-count">(0)</strong>'
),
initialize: function () {
this.render();
},
render: function () {
$('#conversejs').append(this.$el.html(this.template()));
$('#conversejs').append(this.$el.html(
converse.templates.controlbox_toggle({
'label_toggle': __('Toggle chat')
})
));
return this;
},

24
main.js
View File

@ -1,4 +1,4 @@
require.config({
config = {
paths: {
"jquery": "components/jquery/jquery",
"locales": "locale/locales",
@ -6,6 +6,9 @@ require.config({
"underscore": "components/underscore/underscore",
"backbone": "components/backbone/backbone",
"backbone.localStorage": "components/backbone.localStorage/backbone.localStorage",
"text": 'components/requirejs-text/text',
"tpl": 'components/requirejs-tpl-jfparadis/tpl',
"converse-templates": "src/templates",
"strophe": "components/strophe/strophe",
"strophe.muc": "components/strophe.muc/index",
"strophe.roster": "components/strophe.roster/index",
@ -30,6 +33,14 @@ require.config({
"converse-dependencies": "src/deps-full"
},
tpl: {
// Use Mustache style syntax for variable interpolation
templateSettings: {
evaluate : /\{\[([\s\S]+?)\]\}/g,
interpolate : /\{\{([\s\S]+?)\}\}/g
}
},
// define module dependencies for modules not using define
shim: {
'backbone': {
@ -51,8 +62,11 @@ require.config({
'strophe.vcard': { deps: ['strophe'] },
'strophe.disco': { deps: ['strophe'] }
}
});
};
require(["jquery", "converse"], function(require, $, converse) {
window.converse = converse;
});
if (typeof(require) !== 'undefined') {
require.config(config);
require(["jquery", "converse"], function(require, $, converse) {
window.converse = converse;
});
}

View File

@ -19,6 +19,10 @@
utils.openContactsPanel();
});
afterEach(function () {
utils.closeAllChatBoxes();
});
it("is created when you click on a roster item", $.proxy(function () {
var i, $el, click, jid, view;
// openControlBox was called earlier, so the controlbox is

View File

@ -14,6 +14,10 @@
utils.createNewChatRoom('lounge', 'dummy');
});
afterEach(function () {
utils.closeAllChatBoxes();
});
it("shows users currently present in the room", $.proxy(function () {
var chatroomview = this.chatboxesview.views['lounge@muc.localhost'],
$participant_list;

View File

@ -164,7 +164,7 @@
expect(this.rosterview.render).toHaveBeenCalled();
expect(converse.emit).toHaveBeenCalledWith('onRosterViewUpdated');
// Check that they are sorted alphabetically
t = this.rosterview.$el.find('dt#pending-xmpp-contacts').siblings('dd.pending-xmpp-contact').text();
t = this.rosterview.$el.find('dt#pending-xmpp-contacts').siblings('dd.pending-xmpp-contact').find('span').text();
expect(t).toEqual(mock.pend_names.slice(0,i+1).sort().join(''));
}
}, converse));
@ -351,7 +351,7 @@
});
expect(this.rosterview.render).toHaveBeenCalled();
// Check that they are sorted alphabetically
t = this.rosterview.$el.find('dt#xmpp-contact-requests').siblings('dd.requesting-xmpp-contact').text().replace(/AcceptDecline/g, '');
t = this.rosterview.$el.find('dt#xmpp-contact-requests').siblings('dd.requesting-xmpp-contact').children('div').text().replace(/AcceptDecline/g, '');
expect(t).toEqual(mock.req_names.slice(0,i+1).sort().join(''));
// When a requesting contact is added, the controlbox must
// be opened.

77
src/templates.js Normal file
View File

@ -0,0 +1,77 @@
define("converse-templates", [
"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_form",
"tpl!src/templates/room_item",
"tpl!src/templates/room_description",
"tpl!src/templates/room_panel",
"tpl!src/templates/chatroom",
"tpl!src/templates/chatarea",
"tpl!src/templates/form_input",
"tpl!src/templates/select_option",
"tpl!src/templates/form_select",
"tpl!src/templates/form_checkbox",
"tpl!src/templates/field",
"tpl!src/templates/occupant",
"tpl!src/templates/roster_item",
"tpl!src/templates/pending_contact",
"tpl!src/templates/requesting_contact",
"tpl!src/templates/requesting_contacts",
"tpl!src/templates/pending_contacts",
"tpl!src/templates/contacts",
"tpl!src/templates/chat_status",
"tpl!src/templates/change_status_message",
"tpl!src/templates/choose_status",
"tpl!src/templates/status_option",
"tpl!src/templates/login_panel",
"tpl!src/templates/controlbox_toggle"
], function () {
return {
action: arguments[0],
message: arguments[1],
new_day: arguments[2],
info: arguments[3],
controlbox: arguments[4],
chatbox: arguments[5],
toolbar: arguments[6],
contacts_tab: arguments[7],
contacts_panel: arguments[8],
chatrooms_tab: arguments[9],
login_tab: arguments[10],
add_contact_dropdown: arguments[11],
add_contact_form: arguments[12],
room_item: arguments[13],
room_description: arguments[14],
room_panel: arguments[15],
chatroom: arguments[16],
chatarea: arguments[17],
form_input: arguments[18],
select_option: arguments[19],
form_select: arguments[20],
form_checkbox: arguments[21],
field: arguments[22],
occupant: arguments[23],
roster_item: arguments[24],
pending_contact: arguments[25],
requesting_contact: arguments[26],
requesting_contacts: arguments[27],
pending_contacts: arguments[28],
contacts: arguments[29],
chat_status: arguments[30],
change_status_message: arguments[31],
choose_status: arguments[32],
status_option: arguments[33],
login_panel: arguments[34],
controlbox_toggle: arguments[35]
};
});

View File

@ -0,0 +1,4 @@
<div class="chat-message {{extra_classes}}">
<span class="chat-message-{{sender}}">{{time}} **{{username}} </span>
<span class="chat-message-content">{{message}}</span>
</div>

View File

@ -0,0 +1,8 @@
<dl class="add-converse-contact dropdown">
<dt id="xmpp-contact-search" class="fancy-dropdown">
<a class="toggle-xmpp-contact-form" href="#"
title="{{label_click_to_chat}}">
<span class="icon-plus"></span>{{label_add_contact}}</a>
</dt>
<dd class="search-xmpp" style="display:none"><ul></ul></dd>
</dl>

View File

@ -0,0 +1,9 @@
<li>
<form class="add-xmpp-contact">
<input type="text"
name="identifier"
class="username"
placeholder="{{label_contact_username}}"/>
<button type="submit">{{label_add}}</button>
</form>
<li>

View File

@ -0,0 +1,5 @@
<form id="set-custom-xmpp-status">
<input type="text" class="custom-xmpp-status" {{status_message}}
placeholder="{{label_custom_status}}"/>
<button type="submit">{{label_save}}</button>
</form>

View File

@ -0,0 +1,11 @@
<div class="xmpp-status">
<a class="choose-xmpp-status {{chat_status}}"
data-value="{{status_message}}"
href="#" title="{{desc_change_status}}">
<span class="icon-{{chat_status}}"></span>{{status_message}}
</a>
<a class="change-xmpp-status-message icon-pencil"
href="#"
title="{{desc_custom_status}}"></a>
</div>

View File

@ -0,0 +1,13 @@
<div class="chat-area">
<div class="chat-content"></div>
<form class="sendXMPPMessage" action="" method="post">
{[ if (show_toolbar) { ]}
<ul class="chat-toolbar no-text-select"></ul>
{[ } ]}
<textarea type="text" class="chat-textarea"
placeholder="{{label_message}}"/>
</form>
</div>
<div class="participants">
<ul class="participant-list"></ul>
</div>

View File

@ -0,0 +1,17 @@
<div class="chat-head chat-head-chatbox">
<a class="close-chatbox-button icon-close"></a>
<a href="{{url}}" target="_blank" class="user">
<div class="chat-title"> {{ fullname }} </div>
</a>
<p class="user-custom-message"><p/>
</div>
<div class="chat-content"></div>
<form class="sendXMPPMessage" action="" method="post">
{[ if (show_toolbar) { ]}
<ul class="chat-toolbar no-text-select"></ul>
{[ } ]}
<textarea
type="text"
class="chat-textarea"
placeholder="{{label_personal_message}}"/>
</form>

View File

@ -0,0 +1,9 @@
<div class="chat-head chat-head-chatroom">
<a class="close-chatbox-button icon-close"></a>
<a class="configure-chatroom-button icon-wrench" style="display:none"></a>
<div class="chat-title"> {{ name }} </div>
<p class="chatroom-topic"><p/>
</div>
<div class="chat-body">
<span class="spinner centered"/>
</div>

View File

@ -0,0 +1 @@
<li><a class="s" href="#chatrooms">{{label_rooms}}</a></li>

View File

@ -0,0 +1,4 @@
<dl id="target" class="dropdown">
<dt id="fancy-xmpp-status-select" class="fancy-dropdown"></dt>
<dd><ul class="xmpp-status-menu"></ul></dd>
</dl>

View File

@ -0,0 +1 @@
<dt id="xmpp-contacts">{{label_contacts}}</dt>

View File

@ -0,0 +1,10 @@
<form class="set-xmpp-status" action="" method="post">
<span id="xmpp-status-holder">
<select id="select-xmpp-status" style="display:none">
<option value="online">{{label_online}}</option>
<option value="dnd">{{label_busy}}</option>
<option value="away">{{label_away}}</option>
<option value="offline">{{label_offline}}</option>
</select>
</span>
</form>

View File

@ -0,0 +1 @@
<li><a class="s current" href="#users">{{label_contacts}}</a></li>

View File

@ -0,0 +1,5 @@
<div class="chat-head oc-chat-head">
<ul id="controlbox-tabs"></ul>
<a class="close-chatbox-button icon-close"></a>
</div>
<div class="controlbox-panes"></div>

View File

@ -0,0 +1,2 @@
<strong class="conn-feedback">{{label_toggle}}</strong>
<strong style="display: none" id="online-count">(0)</strong>

1
src/templates/field.html Normal file
View File

@ -0,0 +1 @@
<field var="{{name}}"><value>{{value}}</value></field>

View File

@ -0,0 +1 @@
<label>{{label}}<input name="{{name}}" type="{{type}}" {{checked}}></label>

View File

@ -0,0 +1 @@
<label>{{label}}<input name="{{name}}" type="{{type}}" value="{{value}}"></label>

View File

@ -0,0 +1 @@
<label>{{label}}<select name="{{name}}">{{options}}</select></label>

1
src/templates/info.html Normal file
View File

@ -0,0 +1 @@
<div class="chat-info">{{message}}</div>

View File

@ -0,0 +1,7 @@
<form id="converse-login">
<label>{{label_username}}</label>
<input type="username" name="jid">
<label>{{label_password}}</label>
<input type="password" name="password">
<input class="login-submit" type="submit" value="{{label_login}}">
</form">

View File

@ -0,0 +1 @@
<li><a class="current" href="#login">{{label_sign_in}}</a></li>

View File

@ -0,0 +1,4 @@
<div class="chat-message {{extra_classes}}">
<span class="chat-message-{{sender}}">{{time}} {{username}}:&nbsp;</span>
<span class="chat-message-content">{{message}}</span>
</div>

View File

@ -0,0 +1 @@
<time class="chat-date" datetime="{{isodate}}">{{datestring}}</time>

View File

@ -0,0 +1,11 @@
<li class="{{role}}"
{[ if (role === "moderator") { ]}
title="{{desc_moderator}}"
{[ } ]}
{[ if (role === "participant") { ]}
title="{{desc_participant}}"
{[ } ]}
{[ if (role === "visitor") { ]}
title="{{desc_visitor}}"
{[ } ]}
>{{nick}}</li>

View File

@ -0,0 +1 @@
<span>{{fullname}}</span> <a class="remove-xmpp-contact icon-remove" title="{{desc_remove}}" href="#"></a>

View File

@ -0,0 +1 @@
<dt id="pending-xmpp-contacts">{{label_pending_contacts}}</dt>

View File

@ -0,0 +1,3 @@
<div>{{fullname}}</div>
<button type="button" class="accept-xmpp-request">{{label_accept}}</button>
<button type="button" class="decline-xmpp-request">{{label_decline}}</button>

View File

@ -0,0 +1 @@
<dt id="xmpp-contact-requests">{{label_contact_requests}}</dt>

View File

@ -0,0 +1,42 @@
<!-- FIXME: check markup in mockup -->
<div class="room-info">
<p class="room-info"><strong>{{label_desc}}</strong> {{desc}}</p>
<p class="room-info"><strong>{{label_occ}}</strong> {{occ}}</p>
<p class="room-info"><strong>{{label_features}}</strong>
<ul>
{[ if (passwordprotected) { ]}
<li class="room-info locked">{{label_requires_auth}}</li>
{[ } ]}
{[ if (hidden) { ]}
<li class="room-info">{{label_hidden}}</li>
{[ } ]}
{[ if (membersonly) { ]}
<li class="room-info">{{label_requires_invite}}</li>
{[ } ]}
{[ if (moderated) { ]}
<li class="room-info">{{label_moderated}}</li>
{[ } ]}
{[ if (nonanonymous) { ]}
<li class="room-info">{{label_non_anon}}</li>
{[ } ]}
{[ if (open) { ]}
<li class="room-info">{{label_open_room}}</li>
{[ } ]}
{[ if (persistent) { ]}
<li class="room-info">{{label_permanent_room}}</li>
{[ } ]}
{[ if (publicroom) { ]}
<li class="room-info">{{label_public}}</li>
{[ } ]}
{[ if (semianonymous) { ]}
<li class="room-info">{{label_semi_anon}}</li>
{[ } ]}
{[ if (temporary) { ]}
<li class="room-info">{{label_temp_room}}</li>
{[ } ]}
{[ if (unmoderated) { ]}
<li class="room-info">{{label_unmoderated}}</li>
{[ } ]}
</ul>
</p>
</div>

View File

@ -0,0 +1,6 @@
<dd class="available-chatroom">
<a class="open-room" data-room-jid="{{jid}}"
title="{{open_title}}" href="#">{{name}}</a>
<a class="room-info icon-room-info" data-room-jid="{{jid}}"
title="{{info_title}}" href="#">&nbsp;</a>
</dd>

View File

@ -0,0 +1,11 @@
<form class="add-chatroom" action="" method="post">
<input type="text" name="chatroom" class="new-chatroom-name"
placeholder="{{label_room_name}}"/>
<input type="text" name="nick" class="new-chatroom-nick"
placeholder="{{label_nickname}}"/>
<input type="{{server_input_type}}" name="server" class="new-chatroom-server"
placeholder="{{label_server}}"/>
<input type="submit" name="join" value="{{label_join}}"/>
<input type="button" name="show" id="show-rooms" value="{{label_show_rooms}}"/>
</form>
<dl id="available-chatrooms"></dl>

View File

@ -0,0 +1,2 @@
<a class="open-chat" title="{{desc_chat}}" href="#"><span class="icon-{{chat_status}}" title="{{desc_status}}"></span>{{fullname}}</a>
<a class="remove-xmpp-contact icon-remove" title="{{desc_remove}}" href="#"></a>

View File

@ -0,0 +1,9 @@
<li>
<form class="search-xmpp-contact">
<input type="text"
name="identifier"
class="username"
placeholder="{{label_contact_name}}"/>
<button type="submit">{{label_search}}</button>
</form>
<li>

View File

@ -0,0 +1 @@
<option value="{{value}}">{{label}}</option>

View File

@ -0,0 +1,6 @@
<li>
<a href="#" class="{{ value }}" data-value="{{ value }}">
<span class="icon-{{ value }}"></span>
{{ text }}
</a>
</li>

View File

@ -0,0 +1,53 @@
{[ if (show_emoticons) { ]}
<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>
{[ } ]}
{[ if (show_call_button) { ]}
<li><a class="toggle-call icon-phone" title="Start a call"></a></li>
{[ } ]}
{[ if (allow_otr) { ]}
<li class="toggle-otr {{otr_status_class}}" title="{{otr_tooltip}}">
<span class="chat-toolbar-text">{{otr_translated_status}}</span>
{[ if (otr_status == UNENCRYPTED) { ]}
<span class="icon-unlocked"></span>
{[ } ]}
{[ if (otr_status == UNVERIFIED) { ]}
<span class="icon-lock"></span>
{[ } ]}
{[ if (otr_status == VERIFIED) { ]}
<span class="icon-lock"></span>
{[ } ]}
{[ if (otr_status == FINISHED) { ]}
<span class="icon-unlocked"></span>
{[ } ]}
<ul>
{[ if (otr_status == UNENCRYPTED) { ]}
<li><a class="start-otr" href="#">{{label_start_encrypted_conversation}}</a></li>
{[ } ]}
{[ if (otr_status != UNENCRYPTED) { ]}
<li><a class="start-otr" href="#">{{label_start_encrypted_conversation}}</a></li>
<li><a class="end-otr" href="#">{{label_end_encrypted_conversation}}</a></li>
<li><a class="auth-otr" data-scheme="smp" href="#">{{label_verify_with_smp}}</a></li>
{[ } ]}
{[ if (otr_status == UNVERIFIED) { ]}
<li><a class="auth-otr" data-scheme="fingerprint" href="#">{{label_verify_with_fingerprints}}</a></li>
{[ } ]}
<li><a href="http://www.cypherpunks.ca/otr/help/3.2.0/levels.php" target="_blank">{{label_whats_this}}</a></li>
</ul>
</li>
{[ } ]}

View File

@ -8,6 +8,7 @@
<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 rel="stylesheet" type="text/css" media="screen" href="converse.css">
<script src="main.js"></script>
<script data-main="tests_main" src="components/requirejs/require.js"></script>
</head>

View File

@ -1,77 +1,24 @@
require.config({
paths: {
"jquery": "components/jquery/jquery",
"locales": "locale/locales",
"jquery.tinysort": "components/tinysort/src/jquery.tinysort",
"underscore": "components/underscore/underscore",
"backbone": "components/backbone/backbone",
"backbone.localStorage": "components/backbone.localStorage/backbone.localStorage",
"strophe": "components/strophe/strophe",
"strophe.muc": "components/strophe.muc/index",
"strophe.roster": "components/strophe.roster/index",
"strophe.vcard": "components/strophe.vcard/index",
"strophe.disco": "components/strophe.disco/index",
"salsa20": "components/otr/build/dep/salsa20",
"bigint": "src/bigint",
"crypto.core": "components/otr/vendor/cryptojs/core",
"crypto.enc-base64": "components/otr/vendor/cryptojs/enc-base64",
"crypto.md5": "components/crypto-js/src/md5",
"crypto.evpkdf": "components/crypto-js/src/evpkdf",
"crypto.cipher-core": "components/otr/vendor/cryptojs/cipher-core",
"crypto.aes": "components/otr/vendor/cryptojs/aes",
"crypto.sha1": "components/otr/vendor/cryptojs/sha1",
"crypto.sha256": "components/otr/vendor/cryptojs/sha256",
"crypto.hmac": "components/otr/vendor/cryptojs/hmac",
"crypto.pad-nopadding": "components/otr/vendor/cryptojs/pad-nopadding",
"crypto.mode-ctr": "components/otr/vendor/cryptojs/mode-ctr",
"crypto": "src/crypto",
"eventemitter": "components/otr/build/dep/eventemitter",
"otr": "components/otr/build/otr",
"converse-dependencies": "src/deps-full",
// Extra test dependencies
"mock": "tests/mock",
"utils": "tests/utils",
"jasmine": "components/jasmine/lib/jasmine-core/jasmine",
"jasmine-html": "components/jasmine/lib/jasmine-core/jasmine-html",
"jasmine-console-reporter": "node_modules/jasmine-reporters/src/jasmine.console_reporter",
"jasmine-junit-reporter": "node_modules/jasmine-reporters/src/jasmine.junit_reporter"
},
// Extra test dependencies
config.paths.mock = "tests/mock";
config.paths.utils = "tests/utils";
config.paths.jasmine = "components/jasmine/lib/jasmine-core/jasmine";
config.paths["jasmine-html"] = "components/jasmine/lib/jasmine-core/jasmine-html";
config.paths["jasmine-console-reporter"] = "node_modules/jasmine-reporters/src/jasmine.console_reporter";
config.paths["jasmine-junit-reporter"] = "node_modules/jasmine-reporters/src/jasmine.junit_reporter";
// define module dependencies for modules not using define
shim: {
'backbone': {
//These script dependencies should be loaded before loading
//backbone.js
deps: [
'underscore',
'jquery'
],
//Once loaded, use the global 'Backbone' as the
//module value.
exports: 'Backbone'
},
'jquery.tinysort': { deps: ['jquery'] },
'strophe': { deps: ['jquery'] },
'underscore': { exports: '_' },
'strophe.muc': { deps: ['strophe', 'jquery'] },
'strophe.roster': { deps: ['strophe'] },
'strophe.vcard': { deps: ['strophe'] },
'strophe.disco': { deps: ['strophe'] },
// Extra test dependencies
'jasmine-html': {
deps: ['jasmine'],
exports: 'jasmine'
},
'jasmine-console-reporter': {
deps: ['jasmine-html'],
exports: 'jasmine'
},
'jasmine-junit-reporter': {
deps: ['jasmine-html'],
exports: 'jasmine'
}
}
});
config.shim['jasmine-html'] = {
deps: ['jasmine'],
exports: 'jasmine'
};
config.shim['jasmine-console-reporter'] = {
deps: ['jasmine-html'],
exports: 'jasmine'
};
config.shim['jasmine-junit-reporter'] = {
deps: ['jasmine-html'],
exports: 'jasmine'
};
require.config(config);
// Polyfill 'bind' which is not available in phantomjs < 2.0
if (!Function.prototype.bind) {