Commit 329746d8 authored by JC Brand's avatar JC Brand

Merge branch 'register'

parents e246335b d690704f
......@@ -29,7 +29,7 @@
"bootstrapJS": "https://raw.githubusercontent.com/jcbrand/bootstrap/7d96a5f60d26c67b5348b270a775518b96a702c8/dist/js/bootstrap.js",
"fontawesome": "~4.1.0",
"typeahead.js": "https://raw.githubusercontent.com/jcbrand/typeahead.js/eedfb10505dd3a20123d1fafc07c1352d83f0ab3/dist/typeahead.jquery.js",
"strophejs-plugins": "https://github.com/strophe/strophejs-plugins.git#a56421ff4ecf0807113ab48c46728715597df599"
"strophejs-plugins": "https://github.com/strophe/strophejs-plugins.git#conversejs"
},
"exportsOverride": {}
}
......@@ -127,8 +127,8 @@
} else {
audio = new Audio("/sounds/msg_received.mp3");
audio.play();
}
}
}
};
var converse = {
......@@ -162,6 +162,26 @@
converse.initialize = function (settings, callback) {
var converse = this;
// Logging
Strophe.log = function (level, msg) { console.log(level+' '+msg); };
Strophe.error = function (msg) {
console.log('ERROR: '+msg);
};
// Add Strophe Namespaces
Strophe.addNamespace('REGISTER', 'jabber:iq:register');
Strophe.addNamespace('XFORM', 'jabber:x:data');
// Add Strophe Statuses
var i = 0;
Object.keys(Strophe.Status).forEach(function (key) {
i = Math.max(i, Strophe.Status[key]);
});
Strophe.Status.REGIFAIL = i + 1;
Strophe.Status.REGISTERED = i + 2;
Strophe.Status.CONFLICT = i + 3;
Strophe.Status.NOTACCEPTABLE = i + 5;
// Constants
// ---------
var UNENCRYPTED = 0;
......@@ -179,13 +199,11 @@
'dnd': 2,
'online': 1
};
var INACTIVE = 'inactive';
var ACTIVE = 'active';
var COMPOSING = 'composing';
var PAUSED = 'paused';
var GONE = 'gone';
var HAS_CSPRNG = ((typeof crypto !== 'undefined') &&
((typeof crypto.randomBytes === 'function') ||
(typeof crypto.getRandomValues === 'function')
......@@ -195,95 +213,58 @@
(typeof OTR !== "undefined") &&
(typeof DSA !== "undefined")
);
var OPENED = 'opened';
var CLOSED = 'closed';
// Default configuration values
// ----------------------------
this.allow_contact_requests = true;
this.allow_dragresize = true;
this.allow_logout = true;
this.allow_muc = true;
this.allow_otr = true;
this.animate = true;
this.auto_list_rooms = false;
this.auto_reconnect = false;
this.auto_subscribe = false;
this.bosh_service_url = undefined; // The BOSH connection manager URL.
this.cache_otr_key = false;
this.debug = false;
this.default_box_height = 324; // The default height, in pixels, for the control box, chat boxes and chatrooms.
this.expose_rid_and_sid = false;
this.forward_messages = false;
this.hide_muc_server = false;
this.hide_offline_users = false;
this.i18n = locales.en;
this.keepalive = false;
this.message_carbons = false;
this.no_trimming = false; // Set to true for phantomjs tests (where browser apparently has no width)
this.play_sounds = false;
this.prebind = false;
this.roster_groups = false;
this.show_controlbox_by_default = false;
this.show_only_online_users = false;
this.show_toolbar = true;
this.storage = 'session';
this.use_otr_by_default = false;
this.use_vcards = true;
this.visible_toolbar_buttons = {
'emoticons': true,
'call': false,
'clear': true,
'toggle_participants': true
var default_settings = {
allow_contact_requests: true,
allow_dragresize: true,
allow_logout: true,
allow_muc: true,
allow_otr: true,
allow_registration: true,
animate: true,
auto_list_rooms: false,
auto_reconnect: false,
auto_subscribe: false,
bosh_service_url: undefined, // The BOSH connection manager URL.
cache_otr_key: false,
debug: false,
default_box_height: 400, // The default height, in pixels, for the control box, chat boxes and chatrooms.
expose_rid_and_sid: false,
forward_messages: false,
hide_muc_server: false,
hide_offline_users: false,
i18n: locales.en,
keepalive: false,
message_carbons: false,
no_trimming: false, // Set to true for phantomjs tests (where browser apparently has no width)
play_sounds: false,
prebind: false,
roster_groups: false,
show_controlbox_by_default: false,
show_only_online_users: false,
show_toolbar: true,
storage: 'session',
use_otr_by_default: false,
use_vcards: true,
visible_toolbar_buttons: {
'emoticons': true,
'call': false,
'clear': true,
'toggle_participants': true
},
xhr_custom_status: false,
xhr_custom_status_url: '',
xhr_user_search: false,
xhr_user_search_url: ''
};
this.xhr_custom_status = false;
this.xhr_custom_status_url = '';
this.xhr_user_search = false;
this.xhr_user_search_url = '';
_.extend(this, default_settings);
// Allow only whitelisted configuration attributes to be overwritten
_.extend(this, _.pick(settings, [
'allow_contact_requests',
'allow_dragresize',
'allow_logout',
'allow_muc',
'allow_otr',
'animate',
'auto_list_rooms',
'auto_reconnect',
'auto_subscribe',
'bosh_service_url',
'cache_otr_key',
'connection',
'debug',
'default_box_height',
'expose_rid_and_sid',
'forward_messages',
'fullname',
'hide_muc_server',
'hide_offline_users',
'i18n',
'jid',
'keepalive',
'message_carbons',
'no_trimming',
'play_sounds',
'prebind',
'rid',
'roster_groups',
'show_controlbox_by_default',
'show_only_online_users',
'show_toolbar',
'sid',
'storage',
'use_otr_by_default',
'use_vcards',
'xhr_custom_status',
'xhr_custom_status_url',
'xhr_user_search',
'xhr_user_search_url'
]));
_.extend(this, _.pick(settings, Object.keys(default_settings)));
if (settings.visible_toolbar_buttons) {
_.extend(
this.visible_toolbar_buttons,
......@@ -351,10 +332,15 @@
// Module-level functions
// ----------------------
this.giveFeedback = function (message, klass) {
$('.conn-feedback').attr('class', 'conn-feedback').text(message);
if (klass) {
$('.conn-feedback').addClass(klass);
}
$('.conn-feedback').each(function (idx, el) {
var $el = $(el);
$el.addClass('conn-feedback').text(message);
if (klass) {
$el.addClass(klass);
} else {
$el.removeClass('error');
}
});
};
this.log = function (txt, level) {
......@@ -440,7 +426,6 @@
};
this.onConnect = function (status, condition, reconnect) {
var $button, $form;
if ((status === Strophe.Status.CONNECTED) ||
(status === Strophe.Status.ATTACHED)) {
if ((typeof reconnect !== 'undefined') && (reconnect)) {
......@@ -451,30 +436,26 @@
converse.onConnected();
}
} else if (status === Strophe.Status.DISCONNECTED) {
converse.giveFeedback(__('Disconnected'), 'error');
if (converse.auto_reconnect) {
converse.reconnect();
} else {
converse.renderLoginPanel();
}
} else if (status === Strophe.Status.Error) {
converse.renderLoginPanel();
converse.giveFeedback(__('Error'), 'error');
} else if (status === Strophe.Status.CONNECTING) {
converse.giveFeedback(__('Connecting'));
} else if (status === Strophe.Status.CONNFAIL) {
converse.renderLoginPanel();
converse.giveFeedback(__('Connection Failed'), 'error');
} else if (status === Strophe.Status.AUTHENTICATING) {
converse.giveFeedback(__('Authenticating'));
} else if (status === Strophe.Status.AUTHFAIL) {
converse.renderLoginPanel();
converse.giveFeedback(__('Authentication Failed'), 'error');
converse.connection.disconnect(__('Authentication Failed'));
} else if (status === Strophe.Status.DISCONNECTING) {
if (!converse.connection.connected) {
converse.renderLoginPanel();
} else {
converse.giveFeedback(__('Disconnecting'), 'error');
}
if (condition) {
converse.giveFeedback(condition, 'error');
}
}
};
......@@ -534,7 +515,7 @@
this.session.browserStorage = new Backbone.BrowserStorage[converse.storage](id);
this.session.fetch();
$(window).on('beforeunload', $.proxy(function () {
if (converse.connection.connected) {
if (converse.connection.authenticated) {
this.setSession();
} else {
this.clearSession();
......@@ -563,7 +544,6 @@
converse.chatboxviews.closeAllChatBoxes(false);
converse.clearSession();
converse.connection.disconnect();
converse.connection.reset();
};
this.registerGlobalEventHandlers = function () {
......@@ -642,16 +622,6 @@
};
this.onConnected = function () {
if (this.debug) {
this.connection.xmlInput = function (body) { console.log(body); };
this.connection.xmlOutput = function (body) { console.log(body); };
Strophe.log = function (level, msg) {
console.log(level+' '+msg);
};
Strophe.error = function (msg) {
console.log('ERROR: '+msg);
};
}
// When reconnecting, there might be some open chat boxes. We don't
// know whether these boxes are of the same account or not, so we
// close them now.
......@@ -1703,6 +1673,7 @@
this.RoomsPanel = Backbone.View.extend({
tagName: 'div',
className: 'controlbox-pane',
id: 'chatrooms',
events: {
'submit form.add-chatroom': 'createChatRoom',
......@@ -1937,6 +1908,14 @@
}
},
giveFeedback: function (message, klass) {
var $el = this.$('.conn-feedback');
$el.addClass('conn-feedback').text(message);
if (klass) {
$el.addClass(klass);
}
},
onConnected: function () {
if (this.model.get('connected')) {
this.render().initRoster();
......@@ -1963,15 +1942,6 @@
b64_sha1('converse.roster.groups'+converse.bare_jid));
converse.rosterview = new converse.RosterView({model: rostergroups});
this.contactspanel.$el.append(converse.rosterview.$el);
// TODO:
// See if we shouldn't also fetch the roster here... otherwise
// the roster is always populated by the rosterHandler method,
// which appears to be a less economic way.
// i.e. from what it seems, only groups are fetched from
// browserStorage, and no contacts.
// XXX: Make sure that if fetch is called, we don't sort on
// each item add...
// converse.roster.fetch()
converse.rosterview.render().fetch().update();
return this;
},
......@@ -1987,15 +1957,29 @@
},
renderLoginPanel: function () {
var $feedback = this.$('.conn-feedback'); // we want to still show any existing feedback.
this.$el.html(converse.templates.controlbox(this.model.toJSON()));
var cfg = {'$parent': this.$el.find('.controlbox-panes'), 'model': this};
if (!this.loginpanel) {
this.loginpanel = new converse.LoginPanel(cfg);
if (converse.allow_registration) {
this.registerpanel = new converse.RegisterPanel(cfg);
}
} else {
this.loginpanel.delegateEvents().initialize(cfg);
if (converse.allow_registration) {
this.registerpanel.delegateEvents().initialize(cfg);
}
}
this.loginpanel.render();
if (converse.allow_registration) {
this.registerpanel.render().$el.hide();
}
this.initDragResize();
if ($feedback.length) {
this.$('.conn-feedback').replaceWith($feedback);
}
return this;
},
renderContactsPanel: function () {
......@@ -2078,7 +2062,8 @@
},
switchTab: function (ev) {
ev.preventDefault();
// TODO: automatically focus the relevant input
if (ev && ev.preventDefault) { ev.preventDefault(); }
var $tab = $(ev.target),
$sibling = $tab.parent().siblings('li').children('a'),
$tab_panel = $($tab.attr('href'));
......@@ -2086,6 +2071,7 @@
$sibling.removeClass('current');
$tab.addClass('current');
$tab_panel.show();
return this;
},
showHelpMessages: function (msgs) {
......@@ -2419,73 +2405,15 @@
var $form= this.$el.find('form.chatroom-form'),
$stanza = $(stanza),
$fields = $stanza.find('field'),
title = $stanza.find('title').text(),
instructions = $stanza.find('instructions').text(),
i, j, options=[], $field, $options,
values=[], $values, value;
var input_types = {
'text-private': 'password',
'text-single': 'textline',
'fixed': 'label',
'boolean': 'checkbox',
'hidden': 'hidden',
'jid-multi': 'textarea',
'list-single': 'dropdown',
'list-multi': 'dropdown'
};
title = $stanza.find('title').text();
$form.find('span.spinner').remove();
$form.append($('<legend>').text(title));
if (instructions != title) {
$form.append($('<p>').text(instructions));
}
for (i=0; i<$fields.length; i++) {
$field = $($fields[i]);
if ($field.attr('type') == 'list-single' || $field.attr('type') == 'list-multi') {
values = [];
$values = $field.children('value');
for (j=0; j<$values.length; j++) {
values.push($($values[j]).text());
}
options = [];
$options = $field.children('option');
for (j=0; j<$options.length; j++) {
value = $($options[j]).find('value').text();
options.push(converse.templates.select_option({
value: value,
label: $($options[j]).attr('label'),
selected: (values.indexOf(value) >= 0)
}));
}
$form.append(converse.templates.form_select({
name: $field.attr('var'),
label: $field.attr('label'),
options: options.join(''),
multiple: ($field.attr('type') == 'list-multi')
}));
} else if ($field.attr('type') == 'fixed') {
$form.append($('<p>').text($field.find('value').text()));
} else if ($field.attr('type') == 'jid-multi') {
$form.append(converse.templates.form_textarea({
name: $field.attr('var'),
label: $field.attr('label') || '',
value: $field.find('value').text()
}));
} else if ($field.attr('type') == 'boolean') {
$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(converse.templates.form_input({
name: $field.attr('var'),
type: input_types[$field.attr('type')],
label: $field.attr('label') || '',
value: $field.find('value').text()
}));
}
$form.append($('<p class="instructions">').text(this.instructions));
}
_.each($fields, function (field) {
$form.append(utils.xForm2webForm(field));
});
$form.append('<input type="submit" value="'+__('Save')+'"/>');
$form.append('<input type="button" value="'+__('Cancel')+'"/>');
$form.on('submit', $.proxy(this.saveConfiguration, this));
......@@ -2499,26 +2427,7 @@
count = $inputs.length,
configArray = [];
$inputs.each(function () {
var $input = $(this), value;
if ($input.is('[type=checkbox]')) {
value = $input.is(':checked') && 1 || 0;
} else if ($input.is('textarea')) {
value = [];
var lines = $input.val().split('\n');
for( var vk=0; vk<lines.length; vk++) {
var val = $.trim(lines[vk]);
if (val === '')
continue;
value.push(val);
}
} else {
value = $input.val();
}
var cnode = $(converse.templates.field({
name: $input.attr('name'),
value: value
}))[0];
configArray.push(cnode);
configArray.push(utils.webForm2xForm(this));
if (!--count) {
converse.connection.muc.saveConfiguration(
that.model.get('jid'),
......@@ -2537,7 +2446,7 @@
},
onConfigSaved: function (stanza) {
// XXX
// TODO: provide feedback
},
onErrorConfigSaved: function (stanza) {
......@@ -3087,7 +2996,9 @@
this.model.each($.proxy(function (model) {
var id = model.get('id');
if (include_controlbox || id !== 'controlbox') {
this.get(id).close();
if (this.get(id)) { // Should always resolve, but shit happens
this.get(id).close();
}
}
}, this));
return this;
......@@ -4550,28 +4461,422 @@
}
});
this.LoginPanel = Backbone.View.extend({
this.RegisterPanel = Backbone.View.extend({
tagName: 'div',
id: "login-dialog",
id: "register",
className: 'controlbox-pane',
events: {
'submit form#converse-login': 'authenticate'
'submit form#converse-register': 'onProviderChosen'
},
connect: function ($form, jid, password) {
if ($form) {
$form.find('input[type=submit]').hide().after('<span class="spinner login-submit"/>');
initialize: function (cfg) {
this.reset();
this.$parent = cfg.$parent;
this.$tabs = cfg.$parent.parent().find('#controlbox-tabs');
this.registerHooks();
},
render: function () {
this.$parent.append(this.$el.html(
converse.templates.register_panel({
'label_domain': __("Your XMPP provider's domain:"),
'label_register': __('Fetch registration form')
})
));
this.$tabs.append(converse.templates.register_tab({label_register: __('Register')}));
return this;
},
registerHooks: function () {
/* Hook into Strophe's _connect_cb, so that we can send an IQ
* requesting the registration fields.
*/
var conn = converse.connection;
var connect_cb = conn._connect_cb.bind(conn);
conn._connect_cb = $.proxy(function (req, callback, raw) {
if (!this._registering) {
connect_cb(req, callback, raw);
} else {
if (this.getRegistrationFields(req, callback, raw)) {
delete this._registering;
}
}
}, this);
},
getRegistrationFields: function (req, _callback, raw) {
/* Send an IQ stanza to the XMPP server asking for the
* registration fields.
*
* Parameters:
* (Strophe.Request) req - The current request
* (Function) callback
*/
converse.log("sendQueryStanza was called");
var conn = converse.connection;
conn.connected = true;
var body = conn._proto._reqToData(req);
if (!body) { return; }
if (conn._proto._connect_cb(body) === Strophe.Status.CONNFAIL) {
return false;
}
var resource = Strophe.getResourceFromJid(jid);
if (!resource) {
jid += '/converse.js-' + Math.floor(Math.random()*139749825).toString();
var register = body.getElementsByTagName("register");
var mechanisms = body.getElementsByTagName("mechanism");
if (register.length === 0 && mechanisms.length === 0) {
conn._proto._no_auth_received(_callback);
return false;
}
if (register.length === 0) {
conn._changeConnectStatus(
Strophe.Status.REGIFAIL,
'Sorry, the given provider does not support in band account registration. Please try with a different provider.');
return true;
}
// Send an IQ stanza to get all required data fields
conn._addSysHandler(this.onRegistrationFields.bind(this), null, "iq", null, null);
conn.send($iq({type: "get"}).c("query", {xmlns: Strophe.NS.REGISTER}).tree());
return true;
},
onRegistrationFields: function (stanza) {
/* Handler for Registration Fields Request.
*
* Parameters:
* (XMLElement) elem - The query stanza.
*/
if (stanza.getElementsByTagName("query").length !== 1) {
converse.connection._changeConnectStatus(Strophe.Status.REGIFAIL, "unknown");
return false;
}
this.setFields(stanza);
this.renderRegistrationForm(stanza);
return false;
},
reset: function (settings) {
var defaults = {
fields: {},
urls: [],
title: "",
instructions: "",
registered: false,
_registering: false,
domain: null,
form_type: null
};
_.extend(this, defaults);
if (settings) {
_.extend(this, _.pick(settings, Object.keys(defaults)));
}
},
onProviderChosen: function (ev) {
/* Callback method that gets called when the user has chosen an
* XMPP provider.
*
* Parameters:
* (Submit Event) ev - Form submission event.
*/
if (ev && ev.preventDefault) { ev.preventDefault(); }
var $form = $(ev.target),
$domain_input = $form.find('input[name=domain]'),
domain = $domain_input.val(),
errors = false;
if (!domain) {
$domain_input.addClass('error');
return;
}
$form.find('input[type=submit]').hide()
.after('<button class="cancel hor_centered">Cancel</button>')
.after('<span class="spinner login-submit"/>')
.after('<p class="info">Requesting a registration form from the XMPP server</p>');
$form.find('button.cancel').on('click', $.proxy(this.cancelRegistration, this));
this.reset({
domain: Strophe.getDomainFromJid(domain),
_registering: true
});
converse.connection.connect(this.domain, "", $.proxy(this.onRegistering, this));
return false;
},
giveFeedback: function (message, klass) {
this.$('.reg-feedback').attr('class', 'reg-feedback').text(message);
if (klass) {
$('.reg-feedback').addClass(klass);
}
},
onRegistering: function (status, error) {
var that;
console.log('onRegistering');
if (_.contains([
Strophe.Status.DISCONNECTED,
Strophe.Status.CONNFAIL,
Strophe.Status.REGIFAIL
], status)) {
converse.log('Problem during registration: Strophe.Status is: '+status);
this.cancelRegistration();
if (error) {
this.giveFeedback(error, 'error');
} else {
this.giveFeedback(__(
'Something went wrong establishing a connection with "%1$s". Are you sure it exists?',
this.domain
), 'error');
}
} else if (status == Strophe.Status.CONFLICT) {
// TODO
converse.log('CONFLICT');
} else if (status == Strophe.Status.NOTACCEPTABLE) {
// TODO
converse.log('NOTACCEPTABLE');
} else if (status == Strophe.Status.REGISTERED) {
converse.log("Registered successfully.");
converse.connection.reset();
that = this;
this.$('form').hide(function () {
$(this).replaceWith('<span class="spinner centered"/>');
if (that.fields.password && that.fields.username) {
// automatically log the user in
converse.connection.connect(
that.fields.username+'@'+that.domain,
that.fields.password,
converse.onConnect
);
converse.chatboxviews.get('controlbox')
.switchTab({target: that.$tabs.find('.current')})
.giveFeedback(__('Now logging you in'));
} else {
converse.chatboxviews.get('controlbox')
.renderLoginPanel()
.giveFeedback(__('Registered successfully'));
}
that.reset();
});
}
},
renderRegistrationForm: function (stanza) {
/* Renders the registration form based on the XForm fields
* received from the XMPP server.
*
* Parameters:
* (XMLElement) stanza - The IQ stanza received from the XMPP server.
*/
var $form= this.$('form'),
$stanza = $(stanza),
$fields;
$form.empty().append(converse.templates.registration_form({
'domain': this.domain,
'title': this.title,
'instructions': this.instructions
}));
if (this.form_type == 'xform') {
$fields = $stanza.find('field');
_.each($fields, function (field) {
$form.append(utils.xForm2webForm($(field), $stanza));
});
} else {
// Show fields
_.each(Object.keys(this.fields), $.proxy(function (key) {
$form.append('<label>'+key+'</label>');
var $input = $('<input placeholder="'+key+'" name="'+key+'"></input>');
if (key === 'password' || key === 'email') {
$input.attr('type', key);
}
$form.append($input);
}, this));
// Show urls
_.each(this.urls, $.proxy(function (url) {
$form.append($('<a target="blank"></a>').attr('href', url).text(url));
}, this));
}
if (this.fields) {
$form.append('<input type="submit" class="submit" value="'+__('Register')+'"/>');
$form.on('submit', $.proxy(this.submitRegistrationForm, this));
$form.append('<input type="button" class="submit" value="'+__('Cancel')+'"/>');
$form.find('input[type=button]').on('click', $.proxy(this.cancelRegistration, this));
} else {
$form.append('<input type="button" class="submit" value="'+__('Return')+'"/>');
$form.find('input[type=button]').on('click', $.proxy(this.cancelRegistration, this));
}
},
reportErrors: function (stanza) {
/* Report back to the user any error messages received from the
* XMPP server after attempted registration.
*
* Parameters:
* (XMLElement) stanza - The IQ stanza received from the
* XMPP server.
*/
var $form= this.$('form'), flash;
var $errmsgs = $(stanza).find('error text');
var $flash = $form.find('.form-errors');
if (!$flash.length) {
flash = '<legend class="form-errors"></legend>';
if ($form.find('p.instructions').length) {
$form.find('p.instructions').append(flash);
} else {
$form.prepend(flash);
}
$flash = $form.find('.form-errors');
} else {
$flash.empty();
}
$errmsgs.each(function (idx, txt) {
$flash.append($('<p>').text($(txt).text()));
});
if (!$errmsgs.length) {
$flash.append($('<p>').text(
__('The provider rejected your registration attempt. '+
'Please check the values you entered for correctness.')));
}
$flash.show();
},
cancelRegistration: function (ev) {
/* Handler, when the user cancels the registration form.
*/
if (ev && ev.preventDefault) { ev.preventDefault(); }
converse.connection.reset();
this.render();
},
submitRegistrationForm : function (ev) {
/* Handler, when the user submits the registration form.
* Provides form error feedback or starts the registration
* process.
*
* Parameters:
* (Event) ev - the submit event.
*/
if (ev && ev.preventDefault) { ev.preventDefault(); }
var $empty_inputs = this.$('input.required:emptyVal');
if ($empty_inputs.length) {
$empty_inputs.addClass('error');
return;
}
var $inputs = $(ev.target).find(':input:not([type=button]):not([type=submit])'),
iq = $iq({type: "set"})
.c("query", {xmlns:Strophe.NS.REGISTER})
.c("x", {xmlns: Strophe.NS.XFORM, type: 'submit'});
$inputs.each(function () {
iq.cnode(utils.webForm2xForm(this)).up();
});
converse.connection._addSysHandler(this._onRegisterIQ.bind(this), null, "iq", null, null);
converse.connection.send(iq);
this.setFields(iq.tree());
},
setFields: function (stanza) {
/* Stores the values that will be sent to the XMPP server
* during attempted registration.
*
* Parameters:
* (XMLElement) stanza - the IQ stanza that will be sent to the XMPP server.
*/
var $query = $(stanza).find('query'), $xform;
if ($query.length > 0) {
$xform = $query.find('x[xmlns="'+Strophe.NS.XFORM+'"]');
if ($xform.length > 0) {
this._setFieldsFromXForm($xform);
} else {
this._setFieldsFromLegacy($query);
}
}
converse.connection.connect(jid, password, converse.onConnect);
},
_setFieldsFromLegacy: function ($query) {
$query.children().each($.proxy(function (idx, field) {
var $field = $(field);
if (field.tagName.toLowerCase() === 'instructions') {
this.instructions = Strophe.getText(field);
return;
} else if (field.tagName.toLowerCase() === 'x') {
if ($field.attr('xmlns') === 'jabber:x:oob') {
$field.find('url').each($.proxy(function (idx, url) {
this.urls.push($(url).text());
}, this));
}
return;
}
this.fields[field.tagName.toLowerCase()] = Strophe.getText(field);
}, this));
this.form_type = 'legacy';
},
_setFieldsFromXForm: function ($xform) {
this.title = $xform.find('title').text();
this.instructions = $xform.find('instructions').text();
$xform.find('field').each($.proxy(function (idx, field) {
var _var = field.getAttribute('var');
if (_var) {
this.fields[_var.toLowerCase()] = $(field).children('value').text();
} else {
// TODO: other option seems to be type="fixed"
console.log("WARNING: Found field we couldn't parse");
}
}, this));
this.form_type = 'xform';
},
_onRegisterIQ: function (stanza) {
/* Callback method that gets called when a return IQ stanza
* is received from the XMPP server, after attempting to
* register a new user.
*
* Parameters:
* (XMLElement) stanza - The IQ stanza.
*/
var i, field, error = null, that,
query = stanza.getElementsByTagName("query");
if (query.length > 0) {
query = query[0];
}
if (stanza.getAttribute("type") === "error") {
converse.log("Registration failed.");
error = stanza.getElementsByTagName("error");
if (error.length !== 1) {
converse.connection._changeConnectStatus(Strophe.Status.REGIFAIL, "unknown");
return false;
}
error = error[0].firstChild.tagName.toLowerCase();
if (error === 'conflict') {
converse.connection._changeConnectStatus(Strophe.Status.CONFLICT, error);
} else if (error === 'not-acceptable') {
converse.connection._changeConnectStatus(Strophe.Status.NOTACCEPTABLE, error);
} else {
converse.connection._changeConnectStatus(Strophe.Status.REGIFAIL, error);
}
this.reportErrors(stanza);
} else {
converse.connection._changeConnectStatus(Strophe.Status.REGISTERED, null);
}
return false;
},
remove: function () {
this.$tabs.empty();
this.$el.parent().empty();
}
});
this.LoginPanel = Backbone.View.extend({
tagName: 'div',
id: "login-dialog",
className: 'controlbox-pane',
events: {
'submit form#converse-login': 'authenticate'
},
initialize: function (cfg) {
cfg.$parent.html(this.$el.html(
converse.templates.login_panel({
'label_username': __('XMPP/Jabber Username:'),
'label_username': __('XMPP Username:'),
'label_password': __('Password:'),
'label_login': __('Log In')
})
......@@ -4616,6 +4921,18 @@
return false;
},
connect: function ($form, jid, password) {
if ($form) {
$form.find('input[type=submit]').hide().after('<span class="spinner login-submit"/>');
}
var resource = Strophe.getResourceFromJid(jid);
if (!resource) {
jid += '/converse.js-' + Math.floor(Math.random()*139749825).toString();
}
converse.connection.connect(jid, password, converse.onConnect);
},
remove: function () {
this.$tabs.empty();
this.$el.parent().empty();
......@@ -4695,9 +5012,17 @@
});
};
this.setUpXMLLogging = function () {
if (this.debug) {
this.connection.xmlInput = function (body) { console.log(body); };
this.connection.xmlOutput = function (body) { console.log(body); };
}
};
this.initConnection = function () {
var rid, sid, jid;
if (this.connection && this.connection.connected) {
this.setUpXMLLogging();
this.onConnected();
} else {
// XXX: it's not yet clear what the order of preference should
......@@ -4712,6 +5037,7 @@
throw("Error: you must supply a value for the bosh_service_url");
}
this.connection = new Strophe.Connection(this.bosh_service_url);
this.setUpXMLLogging();
if (this.prebind) {
if (this.jid && this.sid && this.rid) {
......@@ -4742,10 +5068,14 @@
* connection.
*/
this.initial_presence_sent = false;
this.roster.off().reset(); // Removes roster contacts
if (this.roster) {
this.roster.off().reset(); // Removes roster contacts
}
this.connection.roster._callbacks = []; // Remove all Roster handlers (e.g. rosterHandler)
this.rosterview.model.off().reset(); // Removes roster groups
this.rosterview.undelegateEvents().remove();
if (this.rosterview) {
this.rosterview.model.off().reset(); // Removes roster groups
this.rosterview.undelegateEvents().remove();
}
this.chatboxes.remove(); // Don't call off(), events won't get re-registered upon reconnect.
if (this.features) {
this.features.reset();
......@@ -4781,6 +5111,9 @@
// Initialization
// --------------
// This is the end of the initialize method.
if (settings.connection) {
this.connection = settings.connection;
}
this._initializePlugins();
this._initialize();
this.registerGlobalEventHandlers();
......
......@@ -481,26 +481,29 @@
transform: rotate(359deg);
}
}
span.spinner:before {
#conversejs .spinner:before {
font-size: 24px;
font-family: 'Converse-js' !important;
content: "\231b";
}
span.spinner {
#conversejs .spinner {
-webkit-animation: spin 2s infinite linear;
-moz-animation: spin 2s infinite linear;
-o-animation: spin 2s infinite linear;
animation: spin 2s infinite linear;
width: 100%;
display: block;
text-align: center;
margin: 5px;
}
span.spinner.centered {
#conversejs .centered {
text-align: center;
padding-top: 5em;
display: block;
margin: 5em auto;
}
span.spinner.hor_centered {
#conversejs .hor_centered {
text-align: center;
display: block;
margin: 0 auto;
}
#conversejs #minimized-chats .box-flyout {
position: absolute;
......@@ -737,12 +740,27 @@ span.spinner.hor_centered {
#conversejs div.delayed .chat-message-me {
color: #7EABBB;
}
input.error {
#conversejs input.error {
border: 1px solid red;
}
#conversejs .conn-feedback.error {
#conversejs .error {
color: red;
}
#converse-register .provider-title {
font-size: 115%;
}
#converse-register .provider-score {
width: 178px;
margin-bottom: 8px;
}
#converse-register .form-help .url {
font-weight: bold;
color: #2D617A;
}
#conversejs .reg-feedback {
font-size: 85%;
}
#conversejs .reg-feedback,
#converse-login .conn-feedback {
width: 100%;
text-align: center;
......@@ -807,6 +825,7 @@ dl.add-converse-contact {
#conversejs .fancy-dropdown {
border: 1px solid #ddd;
height: 22px;
text-align: left;
}
#conversejs .fancy-dropdown a.choose-xmpp-status {
width: 155px;
......@@ -926,9 +945,6 @@ dl.add-converse-contact {
display: block;
clear: both;
}
#conversejs .chatroom-form label.label-ta {
height: auto;
}
#conversejs .chatroom-form label input,
#conversejs .chatroom-form label select {
float: right;
......@@ -965,6 +981,7 @@ dl.add-converse-contact {
padding: 0 5px 0 0;
}
#converse-roster {
text-align: left;
width: 100%;
position: relative;
margin: 0.5em 0 0 0;
......@@ -1013,6 +1030,9 @@ dl.add-converse-contact {
#conversejs dd.available-chatroom a.open-room {
width: 148px;
}
#available-chatrooms {
text-align: left;
}
#available-chatrooms dt,
#converse-roster dt {
font-weight: normal;
......@@ -1110,30 +1130,72 @@ dl.add-converse-contact {
width: 300px;
}
#conversejs .controlbox-pane {
padding: 0;
border-bottom-right-radius: 4px;
text-align: center;
background-color: white;
border-bottom-left-radius: 4px;
border-bottom-right-radius: 4px;
border: 0;
font-size: 14px;
height: 289px;
height: calc(100% - 35px);
overflow-y: auto;
padding: 0;
position: absolute;
width: 100%;
}
#conversejs .controlbox-pane dd {
margin-left: 0;
margin-bottom: 0;
padding: 1em;
}
#conversejs .controlbox-pane dd.odd {
background-color: #DCEAC5;
}
#conversejs form#converse-register .title {
font-weight: bold;
}
#conversejs form#converse-register .info {
font-style: italic;
color: green;
font-size: 85%;
margin: 5px 0;
}
#conversejs .form-help,
#conversejs form#converse-register .instructions {
color: gray;
font-size: 85%;
}
#conversejs .form-help:hover,
#conversejs form#converse-register .instructions:hover {
color: #4f4f4f;
}
#conversejs .form-help {
padding-top: 5px;
}
#conversejs form#converse-register .form-errors {
color: red;
display: none;
}
#conversejs form#converse-register,
#conversejs form#converse-login {
background: white;
padding: 2em 0.5em;
padding: 1em 0.5em;
}
#conversejs form#converse-register input,
#conversejs form#converse-login input {
width: 100%;
width: 178px;
height: 30px;
}
#conversejs form#converse-register label,
#conversejs form#converse-login label {
margin-top: 0.5em;
font-weight: bold;
}
#conversejs form#converse-login .login-submit {
#conversejs form#converse-register .login-submit,
#conversejs form#converse-login .login-submit,
#conversejs form#converse-register .submit,
#conversejs form#converse-login .submit {
margin-top: 1em;
height: 30px;
}
#conversejs form.set-xmpp-status {
background: none;
......@@ -1182,11 +1244,11 @@ select#select-xmpp-status {
text-decoration: none;
border-top-right-radius: 4px;
border-top-left-radius: 4px;
color: #666;
color: #888;
text-shadow: 0 1px 0 #fafafa;
}
#conversejs .chat-head #controlbox-tabs li a:hover {
color: black;
color: #4f4f4f;
}
#conversejs .chat-head #controlbox-tabs li a {
background-color: white;
......@@ -1201,21 +1263,6 @@ select#select-xmpp-status {
cursor: default;
color: #666666;
}
#conversejs div#users,
#conversejs div#chatrooms,
#conversejs div#login-dialog,
#conversejs div#settings {
border: 0;
font-size: 14px;
background-color: white;
border-bottom-right-radius: 4px;
border-bottom-left-radius: 4px;
width: 100%;
height: 289px;
height: calc(100% - 35px);
overflow-y: hidden;
position: absolute;
}
#conversejs div#chatrooms {
overflow-y: auto;
}
......@@ -1413,6 +1460,9 @@ input.custom-xmpp-status {
#conversejs .chatbox .dropdown dd.search-xmpp ul li:hover {
background-color: #bed6e5;
}
#conversejs .xmpp-status-menu {
text-align: left;
}
#conversejs .xmpp-status-menu li a {
width: 100%;
}
......@@ -1433,7 +1483,7 @@ input.custom-xmpp-status {
bottom: 6px;
box-shadow: 1px 3px 5px 3px rgba(0, 0, 0, 0.4);
display: block;
height: 324px;
height: 400px;
position: absolute;
}
#conversejs .minimized-chats-flyout {
......
......@@ -16,6 +16,7 @@ h4,
h5,
h6 {
margin: 0 0 35px;
text-transform: uppercase;
font-family: "Montserrat", "Helvetica Neue", Helvetica, Arial, sans-serif;
font-weight: 700;
letter-spacing: 1px;
......
......@@ -260,6 +260,7 @@
play_sounds: true,
roster_groups: true,
show_controlbox_by_default: true,
debug: false,
xhr_user_search: false
});
});
......
......@@ -509,29 +509,32 @@
}
}
span.spinner:before {
#conversejs .spinner:before {
font-size: 24px;
font-family: 'Converse-js' !important;
content: "\231b";
}
span.spinner {
#conversejs .spinner {
-webkit-animation: spin 2s infinite linear;
-moz-animation: spin 2s infinite linear;
-o-animation: spin 2s infinite linear;
animation: spin 2s infinite linear;
width: 100%;
display: block;
text-align: center;
margin: 5px;
}
span.spinner.centered {
#conversejs .centered {
text-align: center;
padding-top: 5em;
display: block;
margin: 5em auto;
}
span.spinner.hor_centered {
#conversejs .hor_centered {
text-align: center;
display: block;
margin: 0 auto;
}
#conversejs #minimized-chats .box-flyout {
......@@ -811,14 +814,33 @@ span.spinner.hor_centered {
color: #7EABBB;
}
input.error {
#conversejs input.error {
border: 1px solid red;
}
#conversejs .conn-feedback.error {
#conversejs .error {
color: red;
}
#converse-register .provider-title {
font-size: 115%;
}
#converse-register .provider-score {
width: 178px;
margin-bottom: 8px;
}
#converse-register .form-help .url {
font-weight: bold;
color: #2D617A;
}
#conversejs .reg-feedback {
font-size: 85%;
}
#conversejs .reg-feedback,
#converse-login .conn-feedback {
width: 100%;
text-align: center;
......@@ -893,6 +915,7 @@ dl.add-converse-contact {
#conversejs .fancy-dropdown {
border:1px solid #ddd;
height: 22px;
text-align: left;
}
#conversejs .fancy-dropdown a.choose-xmpp-status {
......@@ -1074,6 +1097,7 @@ dl.add-converse-contact {
}
#converse-roster {
text-align: left;
width: 100%;
position: relative;
margin: 0.5em 0 0 0;
......@@ -1131,6 +1155,10 @@ dl.add-converse-contact {
width: 148px;
}
#available-chatrooms {
text-align: left;
}
#available-chatrooms dt,
#converse-roster dt {
font-weight: normal;
......@@ -1247,36 +1275,84 @@ dl.add-converse-contact {
}
#conversejs .controlbox-pane {
padding: 0;
border-bottom-right-radius: 4px;
text-align: center;
background-color: white;
border-bottom-left-radius: 4px;
border-bottom-right-radius: 4px;
border: 0;
font-size: 14px;
height: 289px;
height: ~"calc(100% - 35px)";
overflow-y: auto;
padding: 0;
position: absolute;
width: 100%;
}
#conversejs .controlbox-pane dd {
margin-left: 0;
margin-bottom: 0;
padding: 1em;
}
#conversejs .controlbox-pane dd.odd {
background-color: #DCEAC5;
}
#conversejs form#converse-register .title {
font-weight: bold;
}
#conversejs form#converse-register .info {
font-style: italic;
color: green;
font-size: 85%;
margin: 5px 0;
}
#conversejs .form-help,
#conversejs form#converse-register .instructions {
color: gray;
font-size: 85%;
}
#conversejs .form-help:hover,
#conversejs form#converse-register .instructions:hover {
color: #4f4f4f;
}
#conversejs .form-help {
padding-top: 5px;
}
#conversejs form#converse-register .form-errors {
color: red;
display: none;
}
#conversejs form#converse-register,
#conversejs form#converse-login {
background: white;
padding: 2em 0.5em;
padding: 1em 0.5em;
}
#conversejs form#converse-register input,
#conversejs form#converse-login input {
width: 100%;
width: 178px;
height: 30px;
}
#conversejs form#converse-register label,
#conversejs form#converse-login label {
margin-top: 0.5em;
font-weight: bold;
}
#conversejs form#converse-login .login-submit {
#conversejs form#converse-register .login-submit,
#conversejs form#converse-login .login-submit,
#conversejs form#converse-register .submit,
#conversejs form#converse-login .submit {
margin-top: 1em;
height: 30px;
}
#conversejs form.set-xmpp-status {
......@@ -1334,12 +1410,12 @@ select#select-xmpp-status {
text-decoration: none;
border-top-right-radius: 4px;
border-top-left-radius: 4px;
color: #666;
color: #888;
text-shadow: 0 1px 0 rgba(250, 250, 250, 1);
}
#conversejs .chat-head #controlbox-tabs li a:hover {
color: black;
color: #4f4f4f
}
#conversejs .chat-head #controlbox-tabs li a {
......@@ -1357,22 +1433,6 @@ select#select-xmpp-status {
color: rgb(102,102,102);
}
#conversejs div#users,
#conversejs div#chatrooms,
#conversejs div#login-dialog,
#conversejs div#settings {
border: 0;
font-size: 14px;
background-color: white;
border-bottom-right-radius: 4px;
border-bottom-left-radius: 4px;
width: 100%;
height: 289px;
height: ~"calc(100% - 35px)";
overflow-y: hidden;
position: absolute;
}
#conversejs div#chatrooms {
overflow-y: auto;
}
......@@ -1607,6 +1667,10 @@ input.custom-xmpp-status {
background-color: #bed6e5;
}
#conversejs .xmpp-status-menu {
text-align: left;
}
#conversejs .xmpp-status-menu li a {
width: 100%;
}
......@@ -1631,7 +1695,7 @@ input.custom-xmpp-status {
bottom: 6px;
box-shadow: 1px 3px 5px 3px rgba(0,0,0,0.4);
display: block;
height: 324px;
height: 400px;
position: absolute;
}
......
var config;
if (typeof(require) === 'undefined') {
/* XXX: Hack to work around r.js's stupid parsing.
* We want to save the configuration in a variable so that we can reuse it in
* tests/main.js.
*/
require = {
config: function (c) {
config = c;
}
};
}
require.config({
baseUrl: '.',
paths: {
......@@ -79,9 +92,11 @@ require.config({
"controlbox": "src/templates/controlbox",
"controlbox_toggle": "src/templates/controlbox_toggle",
"field": "src/templates/field",
"form_captcha": "src/templates/form_captcha",
"form_checkbox": "src/templates/form_checkbox",
"form_input": "src/templates/form_input",
"form_select": "src/templates/form_select",
"form_textarea": "src/templates/form_textarea",
"group_header": "src/templates/group_header",
"info": "src/templates/info",
"login_panel": "src/templates/login_panel",
......@@ -91,6 +106,9 @@ require.config({
"occupant": "src/templates/occupant",
"pending_contact": "src/templates/pending_contact",
"pending_contacts": "src/templates/pending_contacts",
"register_panel": "src/templates/register_panel",
"register_tab": "src/templates/register_tab",
"registration_form": "src/templates/registration_form",
"requesting_contact": "src/templates/requesting_contact",
"requesting_contacts": "src/templates/requesting_contacts",
"room_description": "src/templates/room_description",
......@@ -103,8 +121,7 @@ require.config({
"status_option": "src/templates/status_option",
"toggle_chats": "src/templates/toggle_chats",
"toolbar": "src/templates/toolbar",
"trimmed_chat": "src/templates/trimmed_chat",
"form_textarea": "src/templates/form_textarea"
"trimmed_chat": "src/templates/trimmed_chat"
},
map: {
......@@ -143,10 +160,15 @@ require.config({
'strophe': { exports: 'Strophe' },
'strophe.disco': { deps: ['strophe'] },
'strophe.muc': { deps: ['strophe'] },
'strophe.register': { deps: ['strophe'] },
'strophe.roster': { deps: ['strophe'] },
'strophe.vcard': { deps: ['strophe'] }
}
});
require(["converse"], function(converse) {
window.converse = converse;
});
if (typeof(require) === 'function') {
require(config);
require(["converse"], function(converse) {
window.converse = converse;
});
}
......@@ -31,16 +31,26 @@
<div class="chat-head controlbox-head">
<ul id="controlbox-tabs">
<li><a class="current" href="#login">Sign in</a></li>
<li><a href="#register">Register</a></li>
</ul>
<a class="close-chatbox-button icon-close"></a>
</div>
<div id="login-dialog">
<div id="login-dialog" class="controlbox-pane">
<form id="converse-login">
<label>XMPP/Jabber Username:</label><input type="text" id="jid">
<label>XMPP Username:</label><input type="text" id="jid" placeholder="name@server">
<label>Password:</label><input type="password" id="password">
<input class="login-submit" type="submit" value="Log In">
<input class="submit" type="submit" value="Log In">
<span class="conn-feedback"></span>
</form>
</div>
<div id="register" class="controlbox-pane" style="display: none">
<form id="converse-register">
<label>XMPP Username:</label><input type="text" id="jid" placeholder="name@server">
<label>Password:</label><input type="password" name="password">
<label>Confirm Password:</label><input type="password" name="password_confirm">
<input class="submit" type="submit" value="Register">
<span class="conn-feedback"></span>
</form>
<span class="conn-feedback"></span>
</div>
</div>
</div>
......@@ -55,6 +65,7 @@
</ul>
<a class="close-chatbox-button icon-close"></a>
</div>
<div id="users" class="controlbox-pane" style="display: block;">
<form class="set-xmpp-status" action="" method="post">
<span id="xmpp-status-holder">
......@@ -227,39 +238,41 @@
</dd>
</dl>
</div>
<div id="chatrooms" style="display: none;">
<form class="add-chatroom" action="" method="post">
<input type="text" name="chatroom" class="new-chatroom-name" placeholder="Room name">
<input type="text" name="nick" class="new-chatroom-nick" placeholder="Nickname">
<input type="text" name="server" class="new-chatroom-server" placeholder="Server">
<input type="submit" name="join" value="Join">
<input type="button" name="show" id="show-rooms" value="Show rooms" style="display: inline-block;">
</form>
<dl id="available-chatrooms">
<dt>Rooms on conference.opkode.im</dt>
<dd class="available-chatroom">
<a class="open-room"
data-room-jid="converse.js@conference.opkode.im"
title="Click to open this room" href="#">Special chatroom with a long name (2)</a>
<a class="room-info icon-room-info"
data-room-jid="converse.js@conference.opkode.im"
title="Show more information on this room" href="#">&nbsp;</a>
<div class="room-info">
<p class="room-info"><strong>Description:</strong></p>
<p class="room-info"><strong>Occupants:</strong> 2</p>
<p class="room-info"><strong>Features:</strong> </p>
<ul>
<li class="room-info">Moderated</li><li class="room-info">Open room</li>
<li class="room-info">Permanent room</li><li class="room-info">Public</li>
<li class="room-info">Semi-anonymous</li>
<li class="room-info">Requires authentication <span class="icon-lock"></span></li>
<p></p>
</ul>
</div>
</dd>
</dl>
</div>
</div>
<div id="chatrooms" class="controlbox-pane" style="display: none;">
<form class="add-chatroom" action="" method="post">
<input type="text" name="chatroom" class="new-chatroom-name" placeholder="Room name">
<input type="text" name="nick" class="new-chatroom-nick" placeholder="Nickname">
<input type="text" name="server" class="new-chatroom-server" placeholder="Server">
<input type="submit" name="join" value="Join">
<input type="button" name="show" id="show-rooms" value="Show rooms" style="display: inline-block;">
</form>
<dl id="available-chatrooms">
<dt>Rooms on conference.opkode.im</dt>
<dd class="available-chatroom">
<a class="open-room"
data-room-jid="converse.js@conference.opkode.im"
title="Click to open this room" href="#">Special chatroom with a long name (2)</a>
<a class="room-info icon-room-info"
data-room-jid="converse.js@conference.opkode.im"
title="Show more information on this room" href="#">&nbsp;</a>
<div class="room-info">
<p class="room-info"><strong>Description:</strong></p>
<p class="room-info"><strong>Occupants:</strong> 2</p>
<p class="room-info"><strong>Features:</strong> </p>
<ul>
<li class="room-info">Moderated</li><li class="room-info">Open room</li>
<li class="room-info">Permanent room</li><li class="room-info">Public</li>
<li class="room-info">Semi-anonymous</li>
<li class="room-info">Requires authentication <span class="icon-lock"></span></li>
<p></p>
</ul>
</div>
</dd>
</dl>
</div>
</div>
</div>
</div>
......@@ -272,6 +285,12 @@ $(document).ready(function () {
$('a[href=#users]').click(function (ev) {
switchTab(ev);
});
$('a[href=#register]').click(function (ev) {
switchTab(ev);
});
$('a[href=#login]').click(function (ev) {
switchTab(ev);
});
$("a.choose-xmpp-status").click(function (ev) {
ev.preventDefault();
......
......@@ -18,6 +18,7 @@ define("converse-templates", [
"tpl!controlbox_toggle",
"tpl!field",
"tpl!form_checkbox",
"tpl!form_captcha",
"tpl!form_input",
"tpl!form_select",
"tpl!group_header",
......@@ -29,6 +30,9 @@ define("converse-templates", [
"tpl!occupant",
"tpl!pending_contact",
"tpl!pending_contacts",
"tpl!register_panel",
"tpl!register_tab",
"tpl!registration_form",
"tpl!requesting_contact",
"tpl!requesting_contacts",
"tpl!room_description",
......@@ -64,30 +68,34 @@ define("converse-templates", [
controlbox_toggle: arguments[16],
field: arguments[17],
form_checkbox: arguments[18],
form_input: arguments[19],
form_select: arguments[20],
group_header: arguments[21],
info: arguments[22],
login_panel: arguments[23],
login_tab: arguments[24],
message: arguments[25],
new_day: arguments[26],
occupant: arguments[27],
pending_contact: arguments[28],
pending_contacts: arguments[29],
requesting_contact: arguments[30],
requesting_contacts: arguments[31],
room_description: arguments[32],
room_item: arguments[33],
room_panel: arguments[34],
roster: arguments[35],
roster_item: arguments[36],
select_option: arguments[37],
search_contact: arguments[38],
status_option: arguments[39],
toggle_chats: arguments[40],
toolbar: arguments[41],
trimmed_chat: arguments[42],
form_textarea: arguments[43]
form_captcha: arguments[19],
form_input: arguments[20],
form_select: arguments[21],
group_header: arguments[22],
info: arguments[23],
login_panel: arguments[24],
login_tab: arguments[25],
message: arguments[26],
new_day: arguments[27],
occupant: arguments[28],
pending_contact: arguments[29],
pending_contacts: arguments[30],
register_panel: arguments[31],
register_tab: arguments[32],
registration_form: arguments[33],
requesting_contact: arguments[34],
requesting_contacts: arguments[35],
room_description: arguments[36],
room_item: arguments[37],
room_panel: arguments[38],
roster: arguments[39],
roster_item: arguments[40],
select_option: arguments[41],
search_contact: arguments[42],
status_option: arguments[43],
toggle_chats: arguments[44],
toolbar: arguments[45],
trimmed_chat: arguments[46],
form_textarea: arguments[47]
};
});
{[ if (label) { ]}
<label>
{{label}}
</label>
{[ } ]}
<img src="data:{{type}};base64,{{data}}">
<input name="{{name}}" type="text" {[ if (required) { ]} class="required" {[ } ]} >
<label>{{label}}<input name="{{name}}" type="{{type}}" {{checked}}></label>
<label>{{label}}</label>
<input name="{{name}}" type="{{type}}" {{checked}}>
<label>{{label}}<input name="{{name}}" type="{{type}}" value="{{value}}"></label>
{[ if (label) { ]}
<label>
{{label}}
</label>
{[ } ]}
<input name="{{name}}" type="{{type}}"
{[ if (value) { ]} value="{{value}}" {[ } ]}
{[ if (required) { ]} class="required" {[ } ]} >
<label>{{label}}<select name="{{name}}" {[ if (multiple) { ]} multiple="multiple" {[ } ]}>{{options}}</select></label>
<label>{{label}}</label>
<select name="{{name}}" {[ if (multiple) { ]} multiple="multiple" {[ } ]}>{{options}}</select>
<label class="label-ta">{{label}}<textarea name="{{name}}">{{value}}</textarea></label>
<label class="label-ta">{{label}}</label>
<textarea name="{{name}}">{{value}}</textarea>
<form id="converse-login" method="post">
<label>{{label_username}}</label>
<input type="username" name="jid" placeholder="Username">
<input type="username" name="jid" placeholder="user@server">
<label>{{label_password}}</label>
<input type="password" name="password" placeholder="Password">
<input class="login-submit" type="submit" value="{{label_login}}">
<input type="password" name="password" placeholder="password">
<input class="submit" type="submit" value="{{label_login}}">
<span class="conn-feedback"></span>
</form>
<form id="converse-register">
<span class="reg-feedback"></span>
<label>{{label_domain}}</label>
<input type="text" name="domain" placeholder=" e.g. conversejs.org">
<p class="form-help">Tip: A list of public XMPP providers is available <a href="https://xmpp.net/directory.php" class="url" target="_blank">here</a>.</p>
<input class="submit" type="submit" value="{{label_register}}">
</form>
<li><a class="s" href="#register">{{label_register}}</a></li>
<p class="provider-title">{{domain}}</p>
<a href='https://xmpp.net/result.php?domain={{domain}}&amp;type=client'>
<img class="provider-score" src='https://xmpp.net/badge.php?domain={{domain}}' alt='xmpp.net score' />
</a>
<p class="title">{{title}}</p>
<p class="instructions">{{instructions}}</p>
define(["jquery"], function ($) {
define(["jquery", "converse-templates"], function ($, templates) {
"use strict";
var XFORM_TYPE_MAP = {
'text-private': 'password',
'text-single': 'textline',
'fixed': 'label',
'boolean': 'checkbox',
'hidden': 'hidden',
'jid-multi': 'textarea',
'list-single': 'dropdown',
'list-multi': 'dropdown'
};
$.expr[':'].emptyVal = function(obj){
return obj.value === '';
};
$.fn.hasScrollBar = function() {
if (!$.contains(document, this.get(0))) {
return false;
......@@ -52,6 +69,111 @@ define(["jquery"], function ($) {
* See actionInfoMessages
*/
return str;
},
webForm2xForm: function (field) {
/* Takes an HTML DOM and turns it into an XForm field.
*
* Parameters:
* (DOMElement) field - the field to convert
*/
var $input = $(field), value;
if ($input.is('[type=checkbox]')) {
value = $input.is(':checked') && 1 || 0;
} else if ($input.is('textarea')) {
value = [];
var lines = $input.val().split('\n');
for( var vk=0; vk<lines.length; vk++) {
var val = $.trim(lines[vk]);
if (val === '')
continue;
value.push(val);
}
} else {
value = $input.val();
}
return $(templates.field({
name: $input.attr('name'),
value: value
}))[0];
},
xForm2webForm: function ($field, $stanza) {
/* Takes a field in XMPP XForm (XEP-004: Data Forms) format
* and turns it into a HTML DOM field.
*
* Parameters:
* (XMLElement) field - the field to convert
*/
// FIXME: take <required> into consideration
var options = [], j, $options, $values, value, values;
if ($field.attr('type') == 'list-single' || $field.attr('type') == 'list-multi') {
values = [];
$values = $field.children('value');
for (j=0; j<$values.length; j++) {
values.push($($values[j]).text());
}
$options = $field.children('option');
for (j=0; j<$options.length; j++) {
value = $($options[j]).find('value').text();
options.push(templates.select_option({
value: value,
label: $($options[j]).attr('label'),
selected: (values.indexOf(value) >= 0),
required: $field.find('required').length
}));
}
return templates.form_select({
name: $field.attr('var'),
label: $field.attr('label'),
options: options.join(''),
multiple: ($field.attr('type') == 'list-multi'),
required: $field.find('required').length
});
} else if ($field.attr('type') == 'fixed') {
return $('<p class="form-help">').text($field.find('value').text());
} else if ($field.attr('type') == 'jid-multi') {
return templates.form_textarea({
name: $field.attr('var'),
label: $field.attr('label') || '',
value: $field.find('value').text(),
required: $field.find('required').length
});
} else if ($field.attr('type') == 'boolean') {
return templates.form_checkbox({
name: $field.attr('var'),
type: XFORM_TYPE_MAP[$field.attr('type')],
label: $field.attr('label') || '',
checked: $field.find('value').text() === "1" && 'checked="1"' || '',
required: $field.find('required').length
});
} else if ($field.attr('type')) {
return templates.form_input({
name: $field.attr('var'),
type: XFORM_TYPE_MAP[$field.attr('type')],
label: $field.attr('label') || '',
value: $field.find('value').text(),
required: $field.find('required').length
});
} else {
if ($field.attr('var') === 'ocr') { // Captcha
return _.reduce(_.map($field.find('uri'),
$.proxy(function (uri) {
return templates.form_captcha({
label: this.$field.attr('label'),
name: this.$field.attr('var'),
data: this.$stanza.find('data[cid="'+uri.textContent.replace(/^cid:/, '')+'"]').text(),
type: uri.getAttribute('type'),
required: this.$field.find('required').length
});
}, {'$stanza': $stanza, '$field': $field})
),
function (memo, num) { return memo + num; }, ''
);
}
}
}
};
return utils;
......
config = {
baseUrl: '.',
paths: {
"backbone": "components/backbone/backbone",
"backbone.browserStorage": "components/backbone.browserStorage/backbone.browserStorage",
"backbone.overview": "components/backbone.overview/backbone.overview",
"bootstrap": "components/bootstrap/dist/js/bootstrap", // XXX: Only required for https://conversejs.org website
"bootstrapJS": "components/bootstrapJS/index", // XXX: Only required for https://conversejs.org website
"converse-dependencies": "src/deps-website",
"converse-templates": "src/templates",
"eventemitter": "components/otr/build/dep/eventemitter",
"jquery": "components/jquery/dist/jquery",
"jquery-private": "src/jquery-private",
"jquery.browser": "components/jquery.browser/index",
"jquery.easing": "components/jquery-easing-original/index", // XXX: Only required for https://conversejs.org website
"moment": "components/momentjs/moment",
"strophe": "components/strophe/strophe",
"strophe.disco": "components/strophejs-plugins/disco/strophe.disco",
"strophe.muc": "components/strophe.muc/index",
"strophe.roster": "src/strophe.roster",
"strophe.vcard": "components/strophejs-plugins/vcard/strophe.vcard",
"text": 'components/requirejs-text/text',
"tpl": 'components/requirejs-tpl-jcbrand/tpl',
"typeahead": "components/typeahead.js/index",
"underscore": "components/underscore/underscore",
"utils": "src/utils",
// Off-the-record-encryption
"bigint": "src/bigint",
"crypto": "src/crypto",
"crypto.aes": "components/otr/vendor/cryptojs/aes",
"crypto.cipher-core": "components/otr/vendor/cryptojs/cipher-core",
"crypto.core": "components/otr/vendor/cryptojs/core",
"crypto.enc-base64": "components/otr/vendor/cryptojs/enc-base64",
"crypto.evpkdf": "components/crypto-js-evanvosberg/src/evpkdf",
"crypto.hmac": "components/otr/vendor/cryptojs/hmac",
"crypto.md5": "components/crypto-js-evanvosberg/src/md5",
"crypto.mode-ctr": "components/otr/vendor/cryptojs/mode-ctr",
"crypto.pad-nopadding": "components/otr/vendor/cryptojs/pad-nopadding",
"crypto.sha1": "components/otr/vendor/cryptojs/sha1",
"crypto.sha256": "components/otr/vendor/cryptojs/sha256",
"salsa20": "components/otr/build/dep/salsa20",
"otr": "src/otr",
// Locales paths
"locales": "locale/locales",
"jed": "components/jed/jed",
"af": "locale/af/LC_MESSAGES/af",
"de": "locale/de/LC_MESSAGES/de",
"en": "locale/en/LC_MESSAGES/en",
"es": "locale/es/LC_MESSAGES/es",
"fr": "locale/fr/LC_MESSAGES/fr",
"he": "locale/he/LC_MESSAGES/he",
"hu": "locale/hu/LC_MESSAGES/hu",
"id": "locale/id/LC_MESSAGES/id",
"it": "locale/it/LC_MESSAGES/it",
"ja": "locale/ja/LC_MESSAGES/ja",
"nl": "locale/nl/LC_MESSAGES/nl",
"pt_BR": "locale/pt_BR/LC_MESSAGES/pt_BR",
"ru": "locale/ru/LC_MESSAGES/ru",
"zh": "locale/zh/LC_MESSAGES/zh",
// Templates
"action": "src/templates/action",
"add_contact_dropdown": "src/templates/add_contact_dropdown",
"add_contact_form": "src/templates/add_contact_form",
"change_status_message": "src/templates/change_status_message",
"chat_status": "src/templates/chat_status",
"chatarea": "src/templates/chatarea",
"chatbox": "src/templates/chatbox",
"chatroom": "src/templates/chatroom",
"chatroom_password_form": "src/templates/chatroom_password_form",
"chatroom_sidebar": "src/templates/chatroom_sidebar",
"chatrooms_tab": "src/templates/chatrooms_tab",
"chats_panel": "src/templates/chats_panel",
"choose_status": "src/templates/choose_status",
"contacts_panel": "src/templates/contacts_panel",
"contacts_tab": "src/templates/contacts_tab",
"controlbox": "src/templates/controlbox",
"controlbox_toggle": "src/templates/controlbox_toggle",
"field": "src/templates/field",
"form_checkbox": "src/templates/form_checkbox",
"form_input": "src/templates/form_input",
"form_select": "src/templates/form_select",
"group_header": "src/templates/group_header",
"info": "src/templates/info",
"login_panel": "src/templates/login_panel",
"login_tab": "src/templates/login_tab",
"message": "src/templates/message",
"new_day": "src/templates/new_day",
"occupant": "src/templates/occupant",
"pending_contact": "src/templates/pending_contact",
"pending_contacts": "src/templates/pending_contacts",
"requesting_contact": "src/templates/requesting_contact",
"requesting_contacts": "src/templates/requesting_contacts",
"room_description": "src/templates/room_description",
"room_item": "src/templates/room_item",
"room_panel": "src/templates/room_panel",
"roster": "src/templates/roster",
"roster_item": "src/templates/roster_item",
"search_contact": "src/templates/search_contact",
"select_option": "src/templates/select_option",
"status_option": "src/templates/status_option",
"toggle_chats": "src/templates/toggle_chats",
"toolbar": "src/templates/toolbar",
"trimmed_chat": "src/templates/trimmed_chat"
},
map: {
// '*' means all modules will get 'jquery-private'
// for their 'jquery' dependency.
'*': { 'jquery': 'jquery-private' },
// 'jquery-private' wants the real jQuery module
// though. If this line was not here, there would
// be an unresolvable cyclic dependency.
'jquery-private': { 'jquery': 'jquery' }
},
tpl: {
// Configuration for requirejs-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: {
'underscore': { exports: '_' },
'crypto.aes': { deps: ['crypto.cipher-core'] },
'crypto.cipher-core': { deps: ['crypto.enc-base64', 'crypto.evpkdf'] },
'crypto.enc-base64': { deps: ['crypto.core'] },
'crypto.evpkdf': { deps: ['crypto.md5'] },
'crypto.hmac': { deps: ['crypto.core'] },
'crypto.md5': { deps: ['crypto.core'] },
'crypto.mode-ctr': { deps: ['crypto.cipher-core'] },
'crypto.pad-nopadding': { deps: ['crypto.cipher-core'] },
'crypto.sha1': { deps: ['crypto.core'] },
'crypto.sha256': { deps: ['crypto.core'] },
'bigint': { deps: ['crypto'] },
'strophe': { exports: 'Strophe' },
'strophe.disco': { deps: ['strophe'] },
'strophe.muc': { deps: ['strophe'] },
'strophe.roster': { deps: ['strophe'] },
'strophe.vcard': { deps: ['strophe'] }
}
};
// Extra test dependencies
config.paths.mock = "tests/mock";
config.paths.test_utils = "tests/utils";
......
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