Commit 2c6f6bfe authored by JC Brand's avatar JC Brand

Merge branch 'soft-dependencie'

parents 45a3e643 e2f1c68c
...@@ -56,6 +56,7 @@ require.config({ ...@@ -56,6 +56,7 @@ require.config({
"converse-notification": "src/converse-notification", "converse-notification": "src/converse-notification",
"converse-otr": "src/converse-otr", "converse-otr": "src/converse-otr",
"converse-ping": "src/converse-ping", "converse-ping": "src/converse-ping",
"converse-pluggable": "src/converse-pluggable",
"converse-register": "src/converse-register", "converse-register": "src/converse-register",
"converse-rosterview": "src/converse-rosterview", "converse-rosterview": "src/converse-rosterview",
"converse-templates": "src/converse-templates", "converse-templates": "src/converse-templates",
...@@ -235,11 +236,11 @@ if (typeof define !== 'undefined') { ...@@ -235,11 +236,11 @@ if (typeof define !== 'undefined') {
// translations that you care about. // translations that you care about.
"converse-chatview", // Renders standalone chat boxes for single user chat "converse-chatview", // Renders standalone chat boxes for single user chat
"converse-controlbox", // The control box
"converse-mam", // XEP-0313 Message Archive Management "converse-mam", // XEP-0313 Message Archive Management
"converse-muc", // XEP-0045 Multi-user chat "converse-muc", // XEP-0045 Multi-user chat
"converse-vcard", // XEP-0054 VCard-temp "converse-vcard", // XEP-0054 VCard-temp
"converse-otr", // Off-the-record encryption for one-on-one messages "converse-otr", // Off-the-record encryption for one-on-one messages
"converse-controlbox", // The control box
"converse-register", // XEP-0077 In-band registration "converse-register", // XEP-0077 In-band registration
"converse-ping", // XEP-0199 XMPP Ping "converse-ping", // XEP-0199 XMPP Ping
"converse-notification",// HTML5 Notifications "converse-notification",// HTML5 Notifications
......
...@@ -2004,9 +2004,9 @@ ...@@ -2004,9 +2004,9 @@
#conversejs .chatroom .box-flyout .chatroom-body .chat-area .chat-content { #conversejs .chatroom .box-flyout .chatroom-body .chat-area .chat-content {
padding: 0 0.5em 0 0.5em; } padding: 0 0.5em 0 0.5em; }
#conversejs .chatroom .box-flyout .chatroom-body .chat-area.full { #conversejs .chatroom .box-flyout .chatroom-body .chat-area.full {
max-width: 100%; } min-width: 100%; }
#conversejs .chatroom .box-flyout .chatroom-body .chat-area.full .new-msgs-indicator { #conversejs .chatroom .box-flyout .chatroom-body .chat-area.full .new-msgs-indicator {
max-width: 100%; } min-width: 100%; }
#conversejs .chatroom .box-flyout .chatroom-body .mentioned { #conversejs .chatroom .box-flyout .chatroom-body .mentioned {
font-weight: bold; } font-weight: bold; }
#conversejs .chatroom .box-flyout .chatroom-body .chat-msg-room { #conversejs .chatroom .box-flyout .chatroom-body .chat-msg-room {
......
...@@ -68,6 +68,7 @@ ...@@ -68,6 +68,7 @@
play_sounds: true, play_sounds: true,
roster_groups: true, roster_groups: true,
show_controlbox_by_default: true, show_controlbox_by_default: true,
strict_plugin_dependencies: false,
chatstate_notification_blacklist: ['mulles@movim.eu'], chatstate_notification_blacklist: ['mulles@movim.eu'],
xhr_user_search: false, xhr_user_search: false,
debug: true debug: true
......
# Changelog # Changelog
## 1.0.3 (Unreleased) ## 1.0.3 (Unreleased)
- Update the plugin architecture to allow plugins to have optional dependencies [jcbrand]
- Bugfix. Login form doesn't render after logging out, when `auto_reconnect = false` [jcbrand] - Bugfix. Login form doesn't render after logging out, when `auto_reconnect = false` [jcbrand]
- Also indicate new day for the first day's messages. [jcbrand] - Also indicate new day for the first day's messages. [jcbrand]
- Chat bot messages don't appear when they have the same ids as their commands. [jcbrand] - Chat bot messages don't appear when they have the same ids as their commands. [jcbrand]
...@@ -10,9 +12,10 @@ ...@@ -10,9 +12,10 @@
- Don't use sound and desktop notifications for OTR messages (when setting up the session) [jcbrand] - Don't use sound and desktop notifications for OTR messages (when setting up the session) [jcbrand]
- New config option [default_state](https://conversejs.org/docs/html/configuration.html#default_state) [jcbrand] - New config option [default_state](https://conversejs.org/docs/html/configuration.html#default_state) [jcbrand]
- New API method `converse.rooms.close()` - New API method `converse.rooms.close()`
- New configuration setting [allow_muc_invites](https://conversejs.org/docs/html/configuration.html#allow-muc-invites) [jcbrand]
- #553 Add processing hints to OTR messages [jcbrand] - #553 Add processing hints to OTR messages [jcbrand]
- #650 Don't ignore incoming messages with same JID as current user (might be MAM archived) [jcbrand] - #650 Don't ignore incoming messages with same JID as current user (might be MAM archived) [jcbrand]
- #656 online users count in minimized chat window on initialization corrected - #656 online users count in minimized chat window on initialization corrected
## 1.0.2 (2016-05-24) ## 1.0.2 (2016-05-24)
......
...@@ -141,6 +141,15 @@ allow_muc ...@@ -141,6 +141,15 @@ allow_muc
Allow multi-user chat (muc) in chatrooms. Setting this to ``false`` will remove Allow multi-user chat (muc) in chatrooms. Setting this to ``false`` will remove
the ``Chatrooms`` tab from the control box. the ``Chatrooms`` tab from the control box.
allow_muc_invitations
---------------------
* Default: ``true``
Allows users to be invited to join MUC chat rooms. An "Invite" widget will
appear in the sidebar of the chat room where you can type in the JID of a user
to invite into the chat room.
allow_otr allow_otr
--------- ---------
......
...@@ -59,9 +59,9 @@ ...@@ -59,9 +59,9 @@
padding: 0 0.5em 0 0.5em; padding: 0 0.5em 0 0.5em;
} }
&.full { &.full {
max-width: 100%; min-width: 100%;
.new-msgs-indicator { .new-msgs-indicator {
max-width: 100%; min-width: 100%;
} }
} }
} }
......
...@@ -406,11 +406,13 @@ ...@@ -406,11 +406,13 @@
describe("A Chat Message", function () { describe("A Chat Message", function () {
beforeEach(function () { beforeEach(function () {
runs(function () { test_utils.closeAllChatBoxes();
test_utils.closeAllChatBoxes(); test_utils.removeControlBox();
}); converse.roster.browserStorage._clear();
waits(250); test_utils.initConverse();
runs(function () {}); test_utils.createContacts('current');
test_utils.openControlBox();
test_utils.openContactsPanel();
}); });
describe("when received from someone else", function () { describe("when received from someone else", function () {
......
...@@ -20,7 +20,7 @@ ...@@ -20,7 +20,7 @@
var connection = converse.connection; var connection = converse.connection;
delete converse.bosh_service_url; delete converse.bosh_service_url;
delete converse.connection; delete converse.connection;
expect(converse.initConnection.bind({})).toThrow( expect(converse.initConnection.bind(converse)).toThrow(
new Error("initConnection: you must supply a value for either the bosh_service_url or websocket_url or both.")); new Error("initConnection: you must supply a value for either the bosh_service_url or websocket_url or both."));
converse.bosh_service_url = url; converse.bosh_service_url = url;
converse.connection = connection; converse.connection = connection;
......
...@@ -17,6 +17,12 @@ ...@@ -17,6 +17,12 @@
// https://xmpp.org/rfcs/rfc3921.html // https://xmpp.org/rfcs/rfc3921.html
describe("The Protocol", $.proxy(function (mock, test_utils) { describe("The Protocol", $.proxy(function (mock, test_utils) {
beforeEach(function () {
test_utils.removeControlBox();
converse.roster.browserStorage._clear();
test_utils.initConverse();
});
describe("Integration of Roster Items and Presence Subscriptions", $.proxy(function (mock, test_utils) { describe("Integration of Roster Items and Presence Subscriptions", $.proxy(function (mock, test_utils) {
/* Some level of integration between roster items and presence /* Some level of integration between roster items and presence
* subscriptions is normally expected by an instant messaging user * subscriptions is normally expected by an instant messaging user
...@@ -48,9 +54,6 @@ ...@@ -48,9 +54,6 @@
*/ */
beforeEach(function () { beforeEach(function () {
test_utils.closeAllChatBoxes(); test_utils.closeAllChatBoxes();
test_utils.removeControlBox();
converse.roster.browserStorage._clear();
test_utils.initConverse();
test_utils.openControlBox(); test_utils.openControlBox();
test_utils.openContactsPanel(); test_utils.openContactsPanel();
}); });
......
...@@ -23,7 +23,7 @@ ...@@ -23,7 +23,7 @@
var Strophe = strophe.Strophe; var Strophe = strophe.Strophe;
return { return {
'initialize': function (settings, callback) { 'initialize': function (settings, callback) {
converse.initialize(settings, callback); return converse.initialize(settings, callback);
}, },
'log': converse.log, 'log': converse.log,
'connection': { 'connection': {
...@@ -133,7 +133,13 @@ ...@@ -133,7 +133,13 @@
} else if (typeof jids === "string") { } else if (typeof jids === "string") {
return converse.wrappedChatBox(converse.chatboxes.getChatBox(jids, true)); return converse.wrappedChatBox(converse.chatboxes.getChatBox(jids, true));
} }
return _.map(jids, _.partial(_.compose(converse.wrappedChatBox, converse.chatboxes.getChatBox.bind(converse.chatboxes)), _, true)); return _.map(jids,
_.partial(
_.compose(
converse.wrappedChatBox.bind(converse), converse.chatboxes.getChatBox.bind(converse.chatboxes)
), _, true
)
);
} }
}, },
'tokens': { 'tokens': {
...@@ -181,7 +187,8 @@ ...@@ -181,7 +187,8 @@
}, },
'plugins': { 'plugins': {
'add': function (name, plugin) { 'add': function (name, plugin) {
converse.plugins[name] = plugin; plugin.__name__ = name;
converse.pluggable.plugins[name] = plugin;
}, },
'remove': function (name) { 'remove': function (name) {
delete converse.plugins[name]; delete converse.plugins[name];
......
...@@ -24,7 +24,7 @@ ...@@ -24,7 +24,7 @@
}; };
converse_api.plugins.add('chatview', { converse_api.plugins.add('converse-chatview', {
overrides: { overrides: {
// Overrides mentioned here will be picked up by converse.js's // Overrides mentioned here will be picked up by converse.js's
......
...@@ -27,7 +27,7 @@ ...@@ -27,7 +27,7 @@
moment = converse_api.env.moment; moment = converse_api.env.moment;
converse_api.plugins.add('controlbox', { converse_api.plugins.add('converse-controlbox', {
overrides: { overrides: {
// Overrides mentioned here will be picked up by converse.js's // Overrides mentioned here will be picked up by converse.js's
...@@ -668,7 +668,7 @@ ...@@ -668,7 +668,7 @@
initialize: function () { initialize: function () {
this.render(); this.render();
this.updateOnlineCount(); this.updateOnlineCount();
converse.on('initialized', function () { converse.on('initialized', function () {
converse.roster.on("add", this.updateOnlineCount, this); converse.roster.on("add", this.updateOnlineCount, this);
converse.roster.on('change', this.updateOnlineCount, this); converse.roster.on('change', this.updateOnlineCount, this);
......
...@@ -23,11 +23,12 @@ ...@@ -23,11 +23,12 @@
"moment_with_locales", "moment_with_locales",
"strophe", "strophe",
"converse-templates", "converse-templates",
"converse-pluggable",
"strophe.disco", "strophe.disco",
"backbone.browserStorage", "backbone.browserStorage",
"backbone.overview", "backbone.overview",
], factory); ], factory);
}(this, function ($, _, dummy, utils, moment, Strophe, templates) { }(this, function ($, _, dummy, utils, moment, Strophe, templates, pluggable) {
/* /*
* Cannot use this due to Safari bug. * Cannot use this due to Safari bug.
* See https://github.com/jcbrand/converse.js/issues/196 * See https://github.com/jcbrand/converse.js/issues/196
...@@ -58,26 +59,31 @@ ...@@ -58,26 +59,31 @@
var event_context = {}; var event_context = {};
var converse = { var converse = {
plugins: {},
initialized_plugins: [],
templates: templates, templates: templates,
emit: function (evt, data) { emit: function (evt, data) {
$(event_context).trigger(evt, data); $(event_context).trigger(evt, data);
}, },
once: function (evt, handler) { once: function (evt, handler) {
$(event_context).one(evt, handler); $(event_context).one(evt, handler);
}, },
on: function (evt, handler) { on: function (evt, handler) {
if (_.contains(['ready', 'initialized'], evt)) { if (_.contains(['ready', 'initialized'], evt)) {
converse.log('Warning: The "'+evt+'" event has been deprecated and will be removed, please use "connected".'); converse.log('Warning: The "'+evt+'" event has been deprecated and will be removed, please use "connected".');
} }
$(event_context).bind(evt, handler); $(event_context).bind(evt, handler);
}, },
off: function (evt, handler) { off: function (evt, handler) {
$(event_context).unbind(evt, handler); $(event_context).unbind(evt, handler);
} }
}; };
// Make converse pluggable
pluggable.enable(converse);
// Module-level constants // Module-level constants
converse.STATUS_WEIGHTS = { converse.STATUS_WEIGHTS = {
'offline': 6, 'offline': 6,
...@@ -126,6 +132,7 @@ ...@@ -126,6 +132,7 @@
converse.initialize = function (settings, callback) { converse.initialize = function (settings, callback) {
"use strict"; "use strict";
var init_deferred = new $.Deferred();
var converse = this; var converse = this;
var unloadevent; var unloadevent;
if ('onpagehide' in window) { if ('onpagehide' in window) {
...@@ -615,7 +622,7 @@ ...@@ -615,7 +622,7 @@
}; };
this.onStatusInitialized = function (deferred) { this.onStatusInitialized = function () {
this.registerIntervalHandler(); this.registerIntervalHandler();
this.roster = new this.RosterContacts(); this.roster = new this.RosterContacts();
this.roster.browserStorage = new Backbone.BrowserStorage[this.storage]( this.roster.browserStorage = new Backbone.BrowserStorage[this.storage](
...@@ -623,20 +630,14 @@ ...@@ -623,20 +630,14 @@
this.chatboxes.onConnected(); this.chatboxes.onConnected();
this.giveFeedback(__('Contacts')); this.giveFeedback(__('Contacts'));
if (typeof this.callback === 'function') { if (typeof this.callback === 'function') {
// A callback method may be passed in via the // XXX: Deprecate in favor of init_deferred
// converse.initialize method. this.callback();
// XXX: Can we use $.Deferred instead of this callback? }
if (this.connection.service === 'jasmine tests') { if (converse.connection.service === 'jasmine tests') {
// XXX: Call back with the internal converse object. This init_deferred.resolve(converse);
// object should never be exposed to production systems. } else {
// 'jasmine tests' is an invalid http bind service value, init_deferred.resolve();
// so we're sure that this is just for tests.
this.callback(this);
} else {
this.callback();
}
} }
deferred.resolve();
converse.emit('initialized'); converse.emit('initialized');
}; };
...@@ -644,7 +645,6 @@ ...@@ -644,7 +645,6 @@
// When reconnecting, there might be some open chat boxes. We don't // 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 // know whether these boxes are of the same account or not, so we
// close them now. // close them now.
var deferred = new $.Deferred();
// XXX: ran into an issue where a returned PubSub BOSH response was // XXX: ran into an issue where a returned PubSub BOSH response was
// not received by the browser. The solution was to flush the // not received by the browser. The solution was to flush the
// connection early on. I don't know what the underlying cause of // connection early on. I don't know what the underlying cause of
...@@ -664,13 +664,11 @@ ...@@ -664,13 +664,11 @@
this.domain = Strophe.getDomainFromJid(this.connection.jid); this.domain = Strophe.getDomainFromJid(this.connection.jid);
this.features = new this.Features(); this.features = new this.Features();
this.enableCarbons(); this.enableCarbons();
this.initStatus().done(_.bind(this.onStatusInitialized, this, deferred)); this.initStatus().done(_.bind(this.onStatusInitialized, this));
converse.emit('connected'); converse.emit('connected');
converse.emit('ready'); // BBB: Will be removed. converse.emit('ready'); // BBB: Will be removed.
return deferred.promise();
}; };
this.RosterContact = Backbone.Model.extend({ this.RosterContact = Backbone.Model.extend({
initialize: function (attributes, options) { initialize: function (attributes, options) {
...@@ -1770,117 +1768,27 @@ ...@@ -1770,117 +1768,27 @@
return this; return this;
}; };
this.wrappedOverride = function (key, value, super_method) {
// We create a partially applied wrapper function, that
// makes sure to set the proper super method when the
// overriding method is called. This is done to enable
// chaining of plugin methods, all the way up to the
// original method.
this._super[key] = super_method;
return value.apply(this, _.rest(arguments, 3));
};
this._overrideAttribute = function (key, plugin) {
// See converse.plugins.override
var value = plugin.overrides[key];
if (typeof value === "function") {
var wrapped_function = _.partial(
converse.wrappedOverride.bind(converse),
key, value, converse[key].bind(converse)
);
converse[key] = wrapped_function;
} else {
converse[key] = value;
}
};
this._extendObject = function (obj, attributes) {
// See converse.plugins.extend
if (!obj.prototype._super) {
obj.prototype._super = {'converse': converse};
}
_.each(attributes, function (value, key) {
if (key === 'events') {
obj.prototype[key] = _.extend(value, obj.prototype[key]);
} else if (typeof value === 'function') {
// We create a partially applied wrapper function, that
// makes sure to set the proper super method when the
// overriding method is called. This is done to enable
// chaining of plugin methods, all the way up to the
// original method.
var wrapped_function = _.partial(
converse.wrappedOverride,
key, value, obj.prototype[key]
);
obj.prototype[key] = wrapped_function;
} else {
obj.prototype[key] = value;
}
});
};
this.initializePlugins = function () {
if (typeof converse._super === 'undefined') {
converse._super = { 'converse': converse };
}
var updateSettings = function (settings) {
/* Helper method which gets put on the plugin and allows it to
* add more user-facing config settings to converse.js.
*/
_.extend(converse.default_settings, settings);
_.extend(converse, settings);
_.extend(converse, _.pick(converse.user_settings, Object.keys(settings)));
};
_.each(_.keys(this.plugins), function (name) {
var plugin = this.plugins[name];
plugin.updateSettings = updateSettings;
if (_.contains(this.initialized_plugins, name)) {
// Don't initialize plugins twice, otherwise we get
// infinite recursion in overridden methods.
return;
}
plugin.converse = converse;
_.each(Object.keys(plugin.overrides || {}), function (key) {
/* We automatically override all methods and Backbone views and
* models that are in the "overrides" namespace.
*/
var msg,
override = plugin.overrides[key];
if (typeof override === "object") {
if (typeof converse[key] === 'undefined') {
msg = "Error: Plugin tried to override "+key+" but it's not found.";
if (converse.strict_plugin_dependencies) {
throw msg;
} else {
converse.log(msg);
return;
}
}
this._extendObject(converse[key], override);
} else {
this._overrideAttribute(key, plugin);
}
}.bind(this));
if (typeof plugin.initialize === "function") {
plugin.initialize.bind(plugin)(this);
}
this.initialized_plugins.push(name);
}.bind(this));
};
// Initialization // Initialization
// -------------- // --------------
// This is the end of the initialize method. // This is the end of the initialize method.
if (settings.connection) { if (settings.connection) {
this.connection = settings.connection; this.connection = settings.connection;
} }
this.initializePlugins(); var updateSettings = function (settings) {
this._initialize(); /* Helper method which gets put on the plugin and allows it to
this.registerGlobalEventHandlers(); * add more user-facing config settings to converse.js.
*/
_.extend(converse.default_settings, settings);
_.extend(converse, settings);
_.extend(converse, _.pick(converse.user_settings, Object.keys(settings)));
};
converse.pluggable.initializePlugins({
'updateSettings': updateSettings,
'converse': converse
});
converse._initialize();
converse.registerGlobalEventHandlers();
return init_deferred.promise();
}; };
return converse; return converse;
})); }));
...@@ -19,7 +19,7 @@ ...@@ -19,7 +19,7 @@
var $ = converse_api.env.jQuery, var $ = converse_api.env.jQuery,
_ = converse_api.env._; _ = converse_api.env._;
converse_api.plugins.add('dragresize', { converse_api.plugins.add('converse-dragresize', {
overrides: { overrides: {
// Overrides mentioned here will be picked up by converse.js's // Overrides mentioned here will be picked up by converse.js's
......
...@@ -36,7 +36,7 @@ ...@@ -36,7 +36,7 @@
return true; return true;
}; };
converse_api.plugins.add('headline', { converse_api.plugins.add('converse-headline', {
overrides: { overrides: {
// Overrides mentioned here will be picked up by converse.js's // Overrides mentioned here will be picked up by converse.js's
......
...@@ -32,7 +32,7 @@ ...@@ -32,7 +32,7 @@
Strophe.addNamespace('RSM', 'http://jabber.org/protocol/rsm'); Strophe.addNamespace('RSM', 'http://jabber.org/protocol/rsm');
converse_api.plugins.add('mam', { converse_api.plugins.add('converse-mam', {
overrides: { overrides: {
// Overrides mentioned here will be picked up by converse.js's // Overrides mentioned here will be picked up by converse.js's
......
...@@ -23,7 +23,7 @@ ...@@ -23,7 +23,7 @@
utils = converse_api.env.utils, utils = converse_api.env.utils,
__ = utils.__.bind(converse); __ = utils.__.bind(converse);
converse_api.plugins.add('minimize', { converse_api.plugins.add('converse-minimize', {
overrides: { overrides: {
// Overrides mentioned here will be picked up by converse.js's // Overrides mentioned here will be picked up by converse.js's
...@@ -218,7 +218,14 @@ ...@@ -218,7 +218,14 @@
getShownChats: function () { getShownChats: function () {
return this.filter(function (view) { return this.filter(function (view) {
return (!view.model.get('minimized') && view.$el.is(':visible')); // The controlbox can take a while to close,
// so we need to check its state. That's why we checked
// the 'closed' state.
return (
!view.model.get('minimized') &&
!view.model.get('closed') &&
view.$el.is(':visible')
);
}); });
}, },
......
...@@ -14,8 +14,7 @@ ...@@ -14,8 +14,7 @@
"converse-core", "converse-core",
"converse-api", "converse-api",
"typeahead", "typeahead",
"converse-chatview", "converse-chatview"
"converse-controlbox"
], factory); ], factory);
}(this, function (converse, converse_api) { }(this, function (converse, converse_api) {
"use strict"; "use strict";
...@@ -43,7 +42,16 @@ ...@@ -43,7 +42,16 @@
Strophe.addNamespace('MUC_ROOMCONF', Strophe.NS.MUC + "#roomconfig"); Strophe.addNamespace('MUC_ROOMCONF', Strophe.NS.MUC + "#roomconfig");
Strophe.addNamespace('MUC_USER', Strophe.NS.MUC + "#user"); Strophe.addNamespace('MUC_USER', Strophe.NS.MUC + "#user");
converse_api.plugins.add('muc', { converse_api.plugins.add('converse-muc', {
/* Optional dependencies are other plugins which might be
* overridden or relied upon, if they exist, otherwise they're ignored.
*
* However, if the setting "strict_plugin_dependencies" is set to true,
* an error will be raised if the plugin is not found.
*
* NB: These plugins need to have already been loaded via require.js.
*/
optional_dependencies: ["converse-controlbox"],
overrides: { overrides: {
// Overrides mentioned here will be picked up by converse.js's // Overrides mentioned here will be picked up by converse.js's
...@@ -66,9 +74,11 @@ ...@@ -66,9 +74,11 @@
Features: { Features: {
addClientFeatures: function () { addClientFeatures: function () {
this._super.addClientFeatures.apply(this, arguments); this._super.addClientFeatures.apply(this, arguments);
converse.connection.disco.addFeature('jabber:x:conference'); // Invites if (converse.allow_muc_invitations) {
if (this.allow_muc) { converse.connection.disco.addFeature('jabber:x:conference'); // Invites
this.connection.disco.addFeature(Strophe.NS.MUC); }
if (converse.allow_muc) {
converse.connection.disco.addFeature(Strophe.NS.MUC);
} }
} }
}, },
...@@ -147,6 +157,7 @@ ...@@ -147,6 +157,7 @@
var converse = this.converse; var converse = this.converse;
// Configuration values for this plugin // Configuration values for this plugin
this.updateSettings({ this.updateSettings({
allow_muc_invitations: true,
allow_muc: true, allow_muc: true,
auto_join_on_invite: false, // Auto-join chatroom on invite auto_join_on_invite: false, // Auto-join chatroom on invite
auto_join_rooms: [], // List of maps {'jid': 'room@example.org', 'nick': 'WizardKing69' }, auto_join_rooms: [], // List of maps {'jid': 'room@example.org', 'nick': 'WizardKing69' },
...@@ -252,16 +263,15 @@ ...@@ -252,16 +263,15 @@
// Bit of a hack, to make sure that the sidebar's state doesn't change // Bit of a hack, to make sure that the sidebar's state doesn't change
this.model.set({hidden_occupants: !this.model.get('hidden_occupants')}); this.model.set({hidden_occupants: !this.model.get('hidden_occupants')});
} }
var $el = this.$('.icon-hide-users');
if (!this.model.get('hidden_occupants')) { if (!this.model.get('hidden_occupants')) {
this.model.save({hidden_occupants: true}); this.model.save({hidden_occupants: true});
$el.removeClass('icon-hide-users').addClass('icon-show-users'); this.$('.icon-hide-users').removeClass('icon-hide-users').addClass('icon-show-users');
this.$('.occupants').addClass('hidden'); this.$('.occupants').addClass('hidden');
this.$('.chat-area').addClass('full'); this.$('.chat-area').addClass('full');
this.scrollDown(); this.scrollDown();
} else { } else {
this.model.save({hidden_occupants: false}); this.model.save({hidden_occupants: false});
$el.removeClass('icon-show-users').addClass('icon-hide-users'); this.$('.icon-show-users').removeClass('icon-show-users').addClass('icon-hide-users');
this.$('.chat-area').removeClass('full'); this.$('.chat-area').removeClass('full');
this.$('div.occupants').removeClass('hidden'); this.$('div.occupants').removeClass('hidden');
this.scrollDown(); this.scrollDown();
...@@ -958,11 +968,15 @@ ...@@ -958,11 +968,15 @@
render: function () { render: function () {
this.$el.html( this.$el.html(
converse.templates.chatroom_sidebar({ converse.templates.chatroom_sidebar({
'allow_muc_invitations': converse.allow_muc_invitations,
'label_invitation': __('Invite'), 'label_invitation': __('Invite'),
'label_occupants': __('Occupants') 'label_occupants': __('Occupants')
}) })
); );
return this.initInviteWidget(); if (converse.allow_muc_invitations) {
return this.initInviteWidget();
}
return this;
}, },
onOccupantAdded: function (item) { onOccupantAdded: function (item) {
...@@ -1354,15 +1368,17 @@ ...@@ -1354,15 +1368,17 @@
}; };
converse.on('chatBoxesFetched', autoJoinRooms); converse.on('chatBoxesFetched', autoJoinRooms);
var onConnected = function () { if (converse.allow_muc_invitations) {
converse.connection.addHandler( var onConnected = function () {
function (message) { converse.connection.addHandler(
converse.onDirectMUCInvitation(message); function (message) {
return true; converse.onDirectMUCInvitation(message);
}, 'jabber:x:conference', 'message'); return true;
}; }, 'jabber:x:conference', 'message');
converse.on('connected', onConnected); };
converse.on('reconnected', onConnected); converse.on('connected', onConnected);
converse.on('reconnected', onConnected);
}
/* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */
...@@ -1397,18 +1413,14 @@ ...@@ -1397,18 +1413,14 @@
} }
var _transform = function (jid) { var _transform = function (jid) {
jid = jid.toLowerCase(); jid = jid.toLowerCase();
var chatroom = converse.chatboxes.get(jid); return converse.wrappedChatBox(converse.chatboxviews.showChat({
if (!chatroom) { 'id': jid,
chatroom = converse.chatboxviews.showChat({ 'jid': jid,
'id': jid, 'name': Strophe.unescapeNode(Strophe.getNodeFromJid(jid)),
'jid': jid, 'nick': nick,
'name': Strophe.unescapeNode(Strophe.getNodeFromJid(jid)), 'type': 'chatroom',
'nick': nick, 'box_id': b64_sha1(jid)
'type': 'chatroom', }));
'box_id': b64_sha1(jid)
});
}
return converse.wrappedChatBox(converse.chatboxes.getChatBox(jid, true));
}; };
if (typeof jids === "undefined") { if (typeof jids === "undefined") {
throw new TypeError('rooms.open: You need to provide at least one JID'); throw new TypeError('rooms.open: You need to provide at least one JID');
......
...@@ -21,7 +21,7 @@ ...@@ -21,7 +21,7 @@
var supports_html5_notification = "Notification" in window; var supports_html5_notification = "Notification" in window;
converse_api.plugins.add('notification', { converse_api.plugins.add('converse-notification', {
initialize: function () { initialize: function () {
/* The initialize function gets called as soon as the plugin is /* The initialize function gets called as soon as the plugin is
......
...@@ -50,7 +50,7 @@ ...@@ -50,7 +50,7 @@
OTR_CLASS_MAPPING[VERIFIED] = 'verified'; OTR_CLASS_MAPPING[VERIFIED] = 'verified';
OTR_CLASS_MAPPING[FINISHED] = 'finished'; OTR_CLASS_MAPPING[FINISHED] = 'finished';
converse_api.plugins.add('otr', { converse_api.plugins.add('converse-otr', {
overrides: { overrides: {
// Overrides mentioned here will be picked up by converse.js's // Overrides mentioned here will be picked up by converse.js's
......
...@@ -22,7 +22,7 @@ ...@@ -22,7 +22,7 @@
// Other necessary globals // Other necessary globals
var _ = converse_api.env._; var _ = converse_api.env._;
converse_api.plugins.add('ping', { converse_api.plugins.add('converse-ping', {
initialize: function () { initialize: function () {
/* The initialize function gets called as soon as the plugin is /* The initialize function gets called as soon as the plugin is
......
/*
* ____ __ __ __ _
* / __ \/ /_ __ ___ ___ ____ _/ /_ / /__ (_)____
* / /_/ / / / / / __ \/ __ \/ __/ / __ \/ / _ \ / / ___/
* / ____/ / /_/ / /_/ / /_/ / /_/ / /_/ / / __/ / (__ )
* /_/ /_/\__,_/\__, /\__, /\__/_/_.___/_/\___(_)_/ /____/
* /____//____/ /___/
*
*/
(function (root, factory) {
define("converse-pluggable", ["jquery", "underscore"], factory);
}(this, function ($, _) {
"use strict";
function Pluggable (plugged) {
this.plugged = plugged;
this.plugged._super = {};
this.plugins = {};
this.initialized_plugins = [];
}
_.extend(Pluggable.prototype, {
wrappedOverride: function (key, value, super_method) {
/* We create a partially applied wrapper function, that
* makes sure to set the proper super method when the
* overriding method is called. This is done to enable
* chaining of plugin methods, all the way up to the
* original method.
*/
if (typeof super_method === "function") {
this._super[key] = super_method.bind(this);
}
return value.apply(this, _.rest(arguments, 3));
},
_overrideAttribute: function (key, plugin) {
/* Overrides an attribute on the original object (the thing being
* plugged into).
*
* If the attribute being overridden is a function, then the original
* function will still be available via the _super attribute.
*
* If the same function is being overridden multiple times, then
* the original function will be available at the end of a chain of
* functions, starting from the most recent override, all the way
* back to the original function, each being referenced by the
* previous' _super attribute.
*
* For example:
*
* plugin2.MyFunc._super.myFunc => * plugin1.MyFunc._super.myFunc => original.myFunc
*/
var value = plugin.overrides[key];
if (typeof value === "function") {
var wrapped_function = _.partial(
this.wrappedOverride, key, value, this.plugged[key]
);
this.plugged[key] = wrapped_function;
} else {
this.plugged[key] = value;
}
},
_extendObject: function (obj, attributes) {
if (!obj.prototype._super) {
// FIXME: make generic
obj.prototype._super = {'converse': this.plugged };
}
_.each(attributes, function (value, key) {
if (key === 'events') {
obj.prototype[key] = _.extend(value, obj.prototype[key]);
} else if (typeof value === 'function') {
// We create a partially applied wrapper function, that
// makes sure to set the proper super method when the
// overriding method is called. This is done to enable
// chaining of plugin methods, all the way up to the
// original method.
var wrapped_function = _.partial(
this.wrappedOverride, key, value, obj.prototype[key]
);
obj.prototype[key] = wrapped_function;
} else {
obj.prototype[key] = value;
}
}.bind(this));
},
loadOptionalDependencies: function (plugin) {
_.each(plugin.optional_dependencies, function (name) {
var dep = this.plugins[name];
if (dep) {
if (_.contains(dep.optional_dependencies, plugin.__name__)) {
// FIXME: circular dependency checking is only one level deep.
throw "Found a circular dependency between the plugins \""+
plugin.__name__+"\" and \""+name+"\"";
}
this.initializePlugin(dep);
} else {
this.throwUndefinedDependencyError(
"Could not find optional dependency \""+name+"\" "+
"for the plugin \""+plugin.__name__+"\". "+
"If it's needed, make sure it's loaded by require.js");
}
}.bind(this));
},
throwUndefinedDependencyError: function (msg) {
if (this.plugged.strict_plugin_dependencies) {
throw msg;
} else {
console.log(msg);
return;
}
},
applyOverrides: function (plugin) {
_.each(Object.keys(plugin.overrides || {}), function (key) {
/* We automatically override all methods and Backbone views and
* models that are in the "overrides" namespace.
*/
var override = plugin.overrides[key];
if (typeof override === "object") {
if (typeof this.plugged[key] === 'undefined') {
this.throwUndefinedDependencyError("Error: Plugin \""+plugin.__name__+"\" tried to override "+key+" but it's not found.");
} else {
this._extendObject(this.plugged[key], override);
}
} else {
this._overrideAttribute(key, plugin);
}
}.bind(this));
},
initializePlugin: function (plugin) {
if (_.contains(this.initialized_plugins, plugin.__name__)) {
// Don't initialize plugins twice, otherwise we get
// infinite recursion in overridden methods.
return;
}
_.extend(plugin, this.properties);
if (plugin.optional_dependencies) {
this.loadOptionalDependencies(plugin);
}
this.applyOverrides(plugin);
if (typeof plugin.initialize === "function") {
plugin.initialize.bind(plugin)(this);
}
this.initialized_plugins.push(plugin.__name__);
},
initializePlugins: function (properties) {
/* The properties variable is an object of attributes and methods
* which will be attached to the plugins.
*/
if (!_.size(this.plugins)) {
return;
}
this.properties = properties;
_.each(_.values(this.plugins), this.initializePlugin.bind(this));
}
});
return {
'enable': function (object) {
/* Call this method to make an object pluggable */
return _.extend(object, {'pluggable': new Pluggable(object)});
}
};
}));
...@@ -40,7 +40,7 @@ ...@@ -40,7 +40,7 @@
Strophe.Status.CONFLICT = i + 3; Strophe.Status.CONFLICT = i + 3;
Strophe.Status.NOTACCEPTABLE = i + 5; Strophe.Status.NOTACCEPTABLE = i + 5;
converse_api.plugins.add('register', { converse_api.plugins.add('converse-register', {
overrides: { overrides: {
// Overrides mentioned here will be picked up by converse.js's // Overrides mentioned here will be picked up by converse.js's
......
...@@ -20,7 +20,7 @@ ...@@ -20,7 +20,7 @@
moment = converse_api.env.moment; moment = converse_api.env.moment;
converse_api.plugins.add('vcard', { converse_api.plugins.add('converse-vcard', {
overrides: { overrides: {
// Overrides mentioned here will be picked up by converse.js's // Overrides mentioned here will be picked up by converse.js's
......
<!-- <div class="occupants"> --> <!-- <div class="occupants"> -->
{[ if (allow_muc_invitations) { ]}
<form class="pure-form room-invite"> <form class="pure-form room-invite">
<input class="invited-contact" placeholder="{{label_invitation}}" type="text"/> <input class="invited-contact" placeholder="{{label_invitation}}" type="text"/>
</form> </form>
{[ } ]}
<p class="occupants-heading">{{label_occupants}}:</p> <p class="occupants-heading">{{label_occupants}}:</p>
<ul class="occupant-list"></ul> <ul class="occupant-list"></ul>
<!-- </div> --> <!-- </div> -->
...@@ -149,7 +149,6 @@ ...@@ -149,7 +149,6 @@
return str; return str;
}, },
isOTRMessage: function (message) { isOTRMessage: function (message) {
var $body = $(message).children('body'), var $body = $(message).children('body'),
text = ($body.length > 0 ? $body.text() : undefined); text = ($body.length > 0 ? $body.text() : undefined);
......
...@@ -56,7 +56,7 @@ require([ ...@@ -56,7 +56,7 @@ require([
jid: 'dummy@localhost', jid: 'dummy@localhost',
password: 'secret', password: 'secret',
debug: true debug: true
}, function (converse) { }).then(function (converse) {
window.converse = converse; window.converse = converse;
window.crypto = { window.crypto = {
getRandomValues: function (buf) { getRandomValues: function (buf) {
...@@ -86,8 +86,6 @@ require([ ...@@ -86,8 +86,6 @@ require([
"spec/register", "spec/register",
"spec/xmppstatus", "spec/xmppstatus",
], function () { ], function () {
// Make sure this callback is only called once.
delete converse.callback;
// Stub the trimChat method. It causes havoc when running with // Stub the trimChat method. It causes havoc when running with
// phantomJS. // phantomJS.
converse.ChatBoxViews.prototype.trimChat = function () {}; converse.ChatBoxViews.prototype.trimChat = function () {};
......
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