Use composition instead of overrides
This commit is contained in:
parent
d2b1f2c97a
commit
1ef29bee4e
@ -189,14 +189,13 @@ The code for it would look something like this:
|
||||
|
||||
// Commonly used utilities and variables can be found under the "env"
|
||||
// namespace of the "converse" global.
|
||||
var Strophe = converse.env.Strophe,
|
||||
$iq = converse.env.$iq,
|
||||
$msg = converse.env.$msg,
|
||||
$pres = converse.env.$pres,
|
||||
$build = converse.env.$build,
|
||||
b64_sha1 = converse.env.b64_sha1,
|
||||
_ = converse.env._,
|
||||
dayjs = converse.env.dayjs;
|
||||
const Strophe = converse.env.Strophe,
|
||||
$iq = converse.env.$iq,
|
||||
$msg = converse.env.$msg,
|
||||
$pres = converse.env.$pres,
|
||||
$build = converse.env.$build,
|
||||
_ = converse.env._,
|
||||
dayjs = converse.env.dayjs;
|
||||
|
||||
These dependencies are closured so that they don't pollute the global
|
||||
namespace, that's why you need to access them in such a way inside the module.
|
||||
@ -300,7 +299,7 @@ In this case, you should first listen for the ``connection`` event, and then do
|
||||
|
||||
converse.plugins.add('myplugin', {
|
||||
initialize: function () {
|
||||
var _converse = this._converse;
|
||||
const _converse = this._converse;
|
||||
|
||||
_converse.api.listen.on('connected', function () {
|
||||
_converse.api.archive.query({'with': 'admin2@localhost'});
|
||||
@ -363,152 +362,141 @@ generated by `generator-conversejs <https://github.com/jcbrand/generator-convers
|
||||
|
||||
.. code-block:: javascript
|
||||
|
||||
(function (root, factory) {
|
||||
if (typeof define === 'function' && define.amd) {
|
||||
// AMD. Register as a module called "myplugin"
|
||||
define(["converse"], factory);
|
||||
} else {
|
||||
// Browser globals. If you're not using a module loader such as require.js,
|
||||
// then this line below executes. Make sure that your plugin's <script> tag
|
||||
// appears after the one from converse.js.
|
||||
factory(converse);
|
||||
}
|
||||
}(this, function (converse) {
|
||||
import converse from "@converse/headless/converse-core";
|
||||
|
||||
// Commonly used utilities and variables can be found under the "env"
|
||||
// namespace of the "converse" global.
|
||||
var Strophe = converse.env.Strophe,
|
||||
$iq = converse.env.$iq,
|
||||
$msg = converse.env.$msg,
|
||||
$pres = converse.env.$pres,
|
||||
$build = converse.env.$build,
|
||||
b64_sha1 = converse.env.b64_sha1,
|
||||
_ = converse.env._,
|
||||
dayjs = converse.env.dayjs;
|
||||
// Commonly used utilities and variables can be found under the "env"
|
||||
// namespace of the "converse" global.
|
||||
const Strophe = converse.env.Strophe,
|
||||
$iq = converse.env.$iq,
|
||||
$msg = converse.env.$msg,
|
||||
$pres = converse.env.$pres,
|
||||
$build = converse.env.$build,
|
||||
_ = converse.env._,
|
||||
dayjs = converse.env.dayjs;
|
||||
|
||||
// The following line registers your plugin.
|
||||
converse.plugins.add("myplugin", {
|
||||
// The following line registers your plugin.
|
||||
converse.plugins.add("myplugin", {
|
||||
|
||||
/* Dependencies are other plugins which might be
|
||||
* overridden or relied upon, and therefore need to be loaded before
|
||||
* this plugin. They are "optional" because they might not be
|
||||
* available, in which case any overrides applicable to them will be
|
||||
* ignored.
|
||||
*
|
||||
* NB: These plugins need to have already been loaded via require.js.
|
||||
*
|
||||
* It's possible to make these dependencies "non-optional".
|
||||
* If the setting "strict_plugin_dependencies" is set to true,
|
||||
* an error will be raised if the plugin is not found.
|
||||
/* Dependencies are other plugins which might be
|
||||
* overridden or relied upon, and therefore need to be loaded before
|
||||
* this plugin. They are "optional" because they might not be
|
||||
* available, in which case any overrides applicable to them will be
|
||||
* ignored.
|
||||
*
|
||||
* NB: These plugins need to have already been imported or loaded,
|
||||
* either in your plugin or somewhere else.
|
||||
*
|
||||
* It's possible to make these dependencies "non-optional".
|
||||
* If the setting "strict_plugin_dependencies" is set to true,
|
||||
* an error will be raised if the plugin is not found.
|
||||
*/
|
||||
'dependencies': [],
|
||||
|
||||
/* Converse.js's plugin mechanism will call the initialize
|
||||
* method on any plugin (if it exists) as soon as the plugin has
|
||||
* been loaded.
|
||||
*/
|
||||
'initialize': function () {
|
||||
/* Inside this method, you have access to the private
|
||||
* `_converse` object.
|
||||
*/
|
||||
'dependencies': [],
|
||||
const _converse = this._converse;
|
||||
_converse.log("The \"myplugin\" plugin is being initialized");
|
||||
|
||||
/* Converse.js's plugin mechanism will call the initialize
|
||||
* method on any plugin (if it exists) as soon as the plugin has
|
||||
* been loaded.
|
||||
/* From the `_converse` object you can get any configuration
|
||||
* options that the user might have passed in via
|
||||
* `converse.initialize`.
|
||||
*
|
||||
* You can also specify new configuration settings for this
|
||||
* plugin, or override the default values of existing
|
||||
* configuration settings. This is done like so:
|
||||
*/
|
||||
_converse.api.settings.update({
|
||||
'initialize_message': 'Initializing myplugin!'
|
||||
});
|
||||
|
||||
/* The user can then pass in values for the configuration
|
||||
* settings when `converse.initialize` gets called.
|
||||
* For example:
|
||||
*
|
||||
* converse.initialize({
|
||||
* "initialize_message": "My plugin has been initialized"
|
||||
* });
|
||||
*/
|
||||
'initialize': function () {
|
||||
/* Inside this method, you have access to the private
|
||||
* `_converse` object.
|
||||
*/
|
||||
var _converse = this._converse;
|
||||
_converse.log("The \"myplugin\" plugin is being initialized");
|
||||
alert(this._converse.initialize_message);
|
||||
|
||||
/* From the `_converse` object you can get any configuration
|
||||
* options that the user might have passed in via
|
||||
* `converse.initialize`.
|
||||
*
|
||||
* You can also specify new configuration settings for this
|
||||
* plugin, or override the default values of existing
|
||||
* configuration settings. This is done like so:
|
||||
*/
|
||||
_converse.api.settings.update({
|
||||
'initialize_message': 'Initializing myplugin!'
|
||||
});
|
||||
/* Besides `_converse.api.settings.update`, there is also a
|
||||
* `_converse.api.promises.add` method, which allows you to
|
||||
* add new promises that your plugin is obligated to fulfill.
|
||||
*
|
||||
* This method takes a string or a list of strings which
|
||||
* represent the promise names:
|
||||
*
|
||||
* _converse.api.promises.add('myPromise');
|
||||
*
|
||||
* Your plugin should then, when appropriate, resolve the
|
||||
* promise by calling `_converse.api.emit`, which will also
|
||||
* emit an event with the same name as the promise.
|
||||
* For example:
|
||||
*
|
||||
* _converse.api.trigger('operationCompleted');
|
||||
*
|
||||
* Other plugins can then either listen for the event
|
||||
* `operationCompleted` like so:
|
||||
*
|
||||
* _converse.api.listen.on('operationCompleted', function { ... });
|
||||
*
|
||||
* or they can wait for the promise to be fulfilled like so:
|
||||
*
|
||||
* _converse.api.waitUntil('operationCompleted', function { ... });
|
||||
*/
|
||||
},
|
||||
|
||||
/* The user can then pass in values for the configuration
|
||||
* settings when `converse.initialize` gets called.
|
||||
* For example:
|
||||
*
|
||||
* converse.initialize({
|
||||
* "initialize_message": "My plugin has been initialized"
|
||||
* });
|
||||
*/
|
||||
alert(this._converse.initialize_message);
|
||||
/* If you want to override some function or a Backbone model or
|
||||
* view defined elsewhere in converse.js, then you do that under
|
||||
* the "overrides" namespace.
|
||||
*/
|
||||
'overrides': {
|
||||
/* For example, the private *_converse* object has a
|
||||
* method "onConnected". You can override that method as follows:
|
||||
*/
|
||||
'onConnected': function () {
|
||||
// Overrides the onConnected method in converse.js
|
||||
|
||||
/* Besides `_converse.api.settings.update`, there is also a
|
||||
* `_converse.api.promises.add` method, which allows you to
|
||||
* add new promises that your plugin is obligated to fulfill.
|
||||
*
|
||||
* This method takes a string or a list of strings which
|
||||
* represent the promise names:
|
||||
*
|
||||
* _converse.api.promises.add('myPromise');
|
||||
*
|
||||
* Your plugin should then, when appropriate, resolve the
|
||||
* promise by calling `_converse.api.emit`, which will also
|
||||
* emit an event with the same name as the promise.
|
||||
* For example:
|
||||
*
|
||||
* _converse.api.trigger('operationCompleted');
|
||||
*
|
||||
* Other plugins can then either listen for the event
|
||||
* `operationCompleted` like so:
|
||||
*
|
||||
* _converse.api.listen.on('operationCompleted', function { ... });
|
||||
*
|
||||
* or they can wait for the promise to be fulfilled like so:
|
||||
*
|
||||
* _converse.api.waitUntil('operationCompleted', function { ... });
|
||||
*/
|
||||
// Top-level functions in "overrides" are bound to the
|
||||
// inner "_converse" object.
|
||||
const _converse = this;
|
||||
|
||||
// Your custom code can come here ...
|
||||
|
||||
// You can access the original function being overridden
|
||||
// via the __super__ attribute.
|
||||
// Make sure to pass on the arguments supplied to this
|
||||
// function and also to apply the proper "this" object.
|
||||
_converse.__super__.onConnected.apply(this, arguments);
|
||||
|
||||
// Your custom code can come here ...
|
||||
},
|
||||
|
||||
/* If you want to override some function or a Backbone model or
|
||||
* view defined elsewhere in converse.js, then you do that under
|
||||
* the "overrides" namespace.
|
||||
/* Override converse.js's XMPPStatus Backbone model so that we can override the
|
||||
* function that sends out the presence stanza.
|
||||
*/
|
||||
'overrides': {
|
||||
/* For example, the private *_converse* object has a
|
||||
* method "onConnected". You can override that method as follows:
|
||||
*/
|
||||
'onConnected': function () {
|
||||
// Overrides the onConnected method in converse.js
|
||||
'XMPPStatus': {
|
||||
'sendPresence': function (type, status_message, jid) {
|
||||
// The "_converse" object is available via the __super__
|
||||
// attribute.
|
||||
const _converse = this.__super__._converse;
|
||||
|
||||
// Top-level functions in "overrides" are bound to the
|
||||
// inner "_converse" object.
|
||||
var _converse = this;
|
||||
// Custom code can come here ...
|
||||
|
||||
// Your custom code can come here ...
|
||||
// You can call the original overridden method, by
|
||||
// accessing it via the __super__ attribute.
|
||||
// When calling it, you need to apply the proper
|
||||
// context as reference by the "this" variable.
|
||||
this.__super__.sendPresence.apply(this, arguments);
|
||||
|
||||
// You can access the original function being overridden
|
||||
// via the __super__ attribute.
|
||||
// Make sure to pass on the arguments supplied to this
|
||||
// function and also to apply the proper "this" object.
|
||||
_converse.__super__.onConnected.apply(this, arguments);
|
||||
|
||||
// Your custom code can come here ...
|
||||
},
|
||||
|
||||
/* Override converse.js's XMPPStatus Backbone model so that we can override the
|
||||
* function that sends out the presence stanza.
|
||||
*/
|
||||
'XMPPStatus': {
|
||||
'sendPresence': function (type, status_message, jid) {
|
||||
// The "_converse" object is available via the __super__
|
||||
// attribute.
|
||||
var _converse = this.__super__._converse;
|
||||
|
||||
// Custom code can come here ...
|
||||
|
||||
// You can call the original overridden method, by
|
||||
// accessing it via the __super__ attribute.
|
||||
// When calling it, you need to apply the proper
|
||||
// context as reference by the "this" variable.
|
||||
this.__super__.sendPresence.apply(this, arguments);
|
||||
|
||||
// Custom code can come here ...
|
||||
}
|
||||
// Custom code can come here ...
|
||||
}
|
||||
}
|
||||
});
|
||||
}));
|
||||
}
|
||||
});
|
||||
|
@ -42,20 +42,67 @@ converse.plugins.add('converse-bookmark-views', {
|
||||
// Overrides mentioned here will be picked up by converse.js's
|
||||
// plugin architecture they will replace existing methods on the
|
||||
// relevant objects or classes.
|
||||
//
|
||||
// New functions which don't exist yet can also be added.
|
||||
|
||||
ChatRoomView: {
|
||||
events: {
|
||||
'click .toggle-bookmark': 'toggleBookmark'
|
||||
},
|
||||
async renderHeading () {
|
||||
this.__super__.renderHeading.apply(this, arguments);
|
||||
const { _converse } = this.__super__;
|
||||
if (_converse.allow_bookmarks) {
|
||||
const supported = await _converse.checkBookmarksSupport();
|
||||
if (supported) {
|
||||
this.renderBookmarkToggle();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
initialize () {
|
||||
this.__super__.initialize.apply(this, arguments);
|
||||
this.model.on('change:bookmarked', this.onBookmarked, this);
|
||||
this.setBookmarkState();
|
||||
initialize () {
|
||||
/* The initialize function gets called as soon as the plugin is
|
||||
* loaded by converse.js's plugin machinery.
|
||||
*/
|
||||
const { _converse } = this,
|
||||
{ __ } = _converse;
|
||||
|
||||
// Configuration values for this plugin
|
||||
// ====================================
|
||||
// Refer to docs/source/configuration.rst for explanations of these
|
||||
// configuration settings.
|
||||
_converse.api.settings.update({
|
||||
hide_open_bookmarks: true,
|
||||
muc_respect_autojoin: true
|
||||
});
|
||||
|
||||
|
||||
Object.assign(_converse, {
|
||||
|
||||
removeBookmarkViaEvent (ev) {
|
||||
/* Remove a bookmark as determined by the passed in
|
||||
* event.
|
||||
*/
|
||||
ev.preventDefault();
|
||||
const name = ev.target.getAttribute('data-bookmark-name');
|
||||
const jid = ev.target.getAttribute('data-room-jid');
|
||||
if (confirm(__("Are you sure you want to remove the bookmark \"%1$s\"?", name))) {
|
||||
_.invokeMap(_converse.bookmarks.where({'jid': jid}), Backbone.Model.prototype.destroy);
|
||||
}
|
||||
},
|
||||
|
||||
addBookmarkViaEvent (ev) {
|
||||
/* Add a bookmark as determined by the passed in
|
||||
* event.
|
||||
*/
|
||||
ev.preventDefault();
|
||||
const jid = ev.target.getAttribute('data-room-jid');
|
||||
const chatroom = _converse.api.rooms.open(jid, {'bring_to_foreground': true});
|
||||
_converse.chatboxviews.get(jid).renderBookmarkForm();
|
||||
},
|
||||
});
|
||||
|
||||
const bookmarkableChatRoomView = {
|
||||
|
||||
renderBookmarkToggle () {
|
||||
if (this.el.querySelector('.chat-head .toggle-bookmark')) {
|
||||
return;
|
||||
@ -80,21 +127,7 @@ converse.plugins.add('converse-bookmark-views', {
|
||||
}
|
||||
},
|
||||
|
||||
async renderHeading () {
|
||||
this.__super__.renderHeading.apply(this, arguments);
|
||||
const { _converse } = this.__super__;
|
||||
if (_converse.allow_bookmarks) {
|
||||
const supported = await _converse.checkBookmarksSupport();
|
||||
if (supported) {
|
||||
this.renderBookmarkToggle();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
onBookmarked () {
|
||||
const { _converse } = this.__super__,
|
||||
{ __ } = _converse;
|
||||
|
||||
const icon = this.el.querySelector('.toggle-bookmark');
|
||||
if (_.isNull(icon)) {
|
||||
return;
|
||||
@ -111,7 +144,6 @@ converse.plugins.add('converse-bookmark-views', {
|
||||
setBookmarkState () {
|
||||
/* Set whether the groupchat is bookmarked or not.
|
||||
*/
|
||||
const { _converse } = this.__super__;
|
||||
if (!_.isUndefined(_converse.bookmarks)) {
|
||||
const models = _converse.bookmarks.where({'jid': this.model.get('jid')});
|
||||
if (!models.length) {
|
||||
@ -125,7 +157,6 @@ converse.plugins.add('converse-bookmark-views', {
|
||||
renderBookmarkForm () {
|
||||
this.hideChatRoomContents();
|
||||
if (!this.bookmark_form) {
|
||||
const { _converse } = this.__super__;
|
||||
this.bookmark_form = new _converse.MUCBookmarkForm({
|
||||
'model': this.model,
|
||||
'chatroomview': this
|
||||
@ -141,7 +172,6 @@ converse.plugins.add('converse-bookmark-views', {
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
}
|
||||
const { _converse } = this.__super__;
|
||||
const models = _converse.bookmarks.where({'jid': this.model.get('jid')});
|
||||
if (!models.length) {
|
||||
this.renderBookmarkForm();
|
||||
@ -151,48 +181,7 @@ converse.plugins.add('converse-bookmark-views', {
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
initialize () {
|
||||
/* The initialize function gets called as soon as the plugin is
|
||||
* loaded by converse.js's plugin machinery.
|
||||
*/
|
||||
const { _converse } = this,
|
||||
{ __ } = _converse;
|
||||
|
||||
// Configuration values for this plugin
|
||||
// ====================================
|
||||
// Refer to docs/source/configuration.rst for explanations of these
|
||||
// configuration settings.
|
||||
_converse.api.settings.update({
|
||||
hide_open_bookmarks: true,
|
||||
muc_respect_autojoin: true
|
||||
});
|
||||
|
||||
|
||||
Object.assign(_converse, {
|
||||
removeBookmarkViaEvent (ev) {
|
||||
/* Remove a bookmark as determined by the passed in
|
||||
* event.
|
||||
*/
|
||||
ev.preventDefault();
|
||||
const name = ev.target.getAttribute('data-bookmark-name');
|
||||
const jid = ev.target.getAttribute('data-room-jid');
|
||||
if (confirm(__("Are you sure you want to remove the bookmark \"%1$s\"?", name))) {
|
||||
_.invokeMap(_converse.bookmarks.where({'jid': jid}), Backbone.Model.prototype.destroy);
|
||||
}
|
||||
},
|
||||
|
||||
addBookmarkViaEvent (ev) {
|
||||
/* Add a bookmark as determined by the passed in
|
||||
* event.
|
||||
*/
|
||||
ev.preventDefault();
|
||||
const jid = ev.target.getAttribute('data-room-jid');
|
||||
const chatroom = _converse.api.rooms.open(jid, {'bring_to_foreground': true});
|
||||
_converse.chatboxviews.get(jid).renderBookmarkForm();
|
||||
},
|
||||
});
|
||||
Object.assign(_converse.ChatRoomView.prototype, bookmarkableChatRoomView);
|
||||
|
||||
|
||||
_converse.MUCBookmarkForm = Backbone.VDOMView.extend({
|
||||
@ -368,6 +357,7 @@ converse.plugins.add('converse-bookmark-views', {
|
||||
}
|
||||
});
|
||||
|
||||
/************************ BEGIN Event Handlers ************************/
|
||||
const initBookmarkViews = async function () {
|
||||
await _converse.api.waitUntil('roomsPanelRendered');
|
||||
_converse.bookmarksview = new _converse.BookmarksView({'model': _converse.bookmarks});
|
||||
@ -381,5 +371,11 @@ converse.plugins.add('converse-bookmark-views', {
|
||||
}
|
||||
|
||||
_converse.api.listen.on('bookmarksInitialized', initBookmarkViews);
|
||||
|
||||
_converse.api.listen.on('chatRoomOpened', view => {
|
||||
view.model.on('change:bookmarked', view.onBookmarked, view);
|
||||
view.setBookmarkState();
|
||||
});
|
||||
/************************ END Event Handlers ************************/
|
||||
}
|
||||
});
|
||||
|
@ -105,23 +105,6 @@ converse.plugins.add('converse-controlbox', {
|
||||
view.close();
|
||||
});
|
||||
return this;
|
||||
},
|
||||
|
||||
getChatBoxWidth (view) {
|
||||
const { _converse } = this.__super__;
|
||||
const controlbox = this.get('controlbox');
|
||||
if (view.model.get('id') === 'controlbox') {
|
||||
/* We return the width of the controlbox or its toggle,
|
||||
* depending on which is visible.
|
||||
*/
|
||||
if (!controlbox || !u.isVisible(controlbox.el)) {
|
||||
return u.getOuterWidth(_converse.controlboxtoggle.el, true);
|
||||
} else {
|
||||
return u.getOuterWidth(controlbox.el, true);
|
||||
}
|
||||
} else {
|
||||
return this.__super__.getChatBoxWidth.apply(this, arguments);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@ -232,11 +215,12 @@ converse.plugins.add('converse-controlbox', {
|
||||
* Triggered when the _converse.ControlBoxView has been initialized and therefore
|
||||
* exists. The controlbox contains the login and register forms when the user is
|
||||
* logged out and a list of the user's contacts and group chats when logged in.
|
||||
* @event _converse#chatBoxInitialized
|
||||
* @event _converse#controlboxInitialized
|
||||
* @type { _converse.ControlBoxView }
|
||||
* @example _converse.api.listen.on('controlboxInitialized', view => { ... });
|
||||
*/
|
||||
_converse.api.trigger('controlboxInitialized', this);
|
||||
_converse.api.trigger('chatBoxInitialized', this);
|
||||
},
|
||||
|
||||
render () {
|
||||
|
@ -45,42 +45,6 @@ converse.plugins.add('converse-dragresize', {
|
||||
// Overrides mentioned here will be picked up by converse.js's
|
||||
// plugin architecture they will replace existing methods on the
|
||||
// relevant objects or classes.
|
||||
//
|
||||
// New functions which don't exist yet can also be added.
|
||||
|
||||
registerGlobalEventHandlers () {
|
||||
const that = this;
|
||||
|
||||
document.addEventListener('mousemove', function (ev) {
|
||||
if (!that.resizing || !that.allow_dragresize) { return true; }
|
||||
ev.preventDefault();
|
||||
that.resizing.chatbox.resizeChatBox(ev);
|
||||
});
|
||||
|
||||
document.addEventListener('mouseup', function (ev) {
|
||||
if (!that.resizing || !that.allow_dragresize) { return true; }
|
||||
ev.preventDefault();
|
||||
const height = that.applyDragResistance(
|
||||
that.resizing.chatbox.height,
|
||||
that.resizing.chatbox.model.get('default_height')
|
||||
);
|
||||
const width = that.applyDragResistance(
|
||||
that.resizing.chatbox.width,
|
||||
that.resizing.chatbox.model.get('default_width')
|
||||
);
|
||||
if (that.connection.connected) {
|
||||
that.resizing.chatbox.model.save({'height': height});
|
||||
that.resizing.chatbox.model.save({'width': width});
|
||||
} else {
|
||||
that.resizing.chatbox.model.set({'height': height});
|
||||
that.resizing.chatbox.model.set({'width': width});
|
||||
}
|
||||
that.resizing = null;
|
||||
});
|
||||
|
||||
return this.__super__.registerGlobalEventHandlers.apply(this, arguments);
|
||||
},
|
||||
|
||||
ChatBox: {
|
||||
initialize () {
|
||||
const { _converse } = this.__super__;
|
||||
@ -102,11 +66,6 @@ converse.plugins.add('converse-dragresize', {
|
||||
'mousedown .dragresize-topleft': 'onStartDiagonalResize'
|
||||
},
|
||||
|
||||
initialize () {
|
||||
window.addEventListener('resize', _.debounce(this.setDimensions.bind(this), 100));
|
||||
this.__super__.initialize.apply(this, arguments);
|
||||
},
|
||||
|
||||
render () {
|
||||
const result = this.__super__.render.apply(this, arguments);
|
||||
renderDragResizeHandles(this.__super__._converse, this);
|
||||
@ -114,152 +73,9 @@ converse.plugins.add('converse-dragresize', {
|
||||
return result;
|
||||
},
|
||||
|
||||
setWidth () {
|
||||
// If a custom width is applied (due to drag-resizing),
|
||||
// then we need to set the width of the .chatbox element as well.
|
||||
if (this.model.get('width')) {
|
||||
this.el.style.width = this.model.get('width');
|
||||
}
|
||||
},
|
||||
|
||||
_show () {
|
||||
this.initDragResize().setDimensions();
|
||||
this.__super__._show.apply(this, arguments);
|
||||
},
|
||||
|
||||
initDragResize () {
|
||||
/* Determine and store the default box size.
|
||||
* We need this information for the drag-resizing feature.
|
||||
*/
|
||||
const { _converse } = this.__super__,
|
||||
flyout = this.el.querySelector('.box-flyout'),
|
||||
style = window.getComputedStyle(flyout);
|
||||
|
||||
if (_.isUndefined(this.model.get('height'))) {
|
||||
const height = parseInt(style.height.replace(/px$/, ''), 10),
|
||||
width = parseInt(style.width.replace(/px$/, ''), 10);
|
||||
this.model.set('height', height);
|
||||
this.model.set('default_height', height);
|
||||
this.model.set('width', width);
|
||||
this.model.set('default_width', width);
|
||||
}
|
||||
const min_width = style['min-width'];
|
||||
const min_height = style['min-height'];
|
||||
this.model.set('min_width', min_width.endsWith('px') ? Number(min_width.replace(/px$/, '')) :0);
|
||||
this.model.set('min_height', min_height.endsWith('px') ? Number(min_height.replace(/px$/, '')) :0);
|
||||
// Initialize last known mouse position
|
||||
this.prev_pageY = 0;
|
||||
this.prev_pageX = 0;
|
||||
if (_converse.connection.connected) {
|
||||
this.height = this.model.get('height');
|
||||
this.width = this.model.get('width');
|
||||
}
|
||||
return this;
|
||||
},
|
||||
|
||||
setDimensions () {
|
||||
// Make sure the chat box has the right height and width.
|
||||
this.adjustToViewport();
|
||||
this.setChatBoxHeight(this.model.get('height'));
|
||||
this.setChatBoxWidth(this.model.get('width'));
|
||||
},
|
||||
|
||||
setChatBoxHeight (height) {
|
||||
const { _converse } = this.__super__;
|
||||
if (height) {
|
||||
height = _converse.applyDragResistance(height, this.model.get('default_height'))+'px';
|
||||
} else {
|
||||
height = "";
|
||||
}
|
||||
const flyout_el = this.el.querySelector('.box-flyout');
|
||||
if (!_.isNull(flyout_el)) {
|
||||
flyout_el.style.height = height;
|
||||
}
|
||||
},
|
||||
|
||||
setChatBoxWidth (width) {
|
||||
const { _converse } = this.__super__;
|
||||
if (width) {
|
||||
width = _converse.applyDragResistance(width, this.model.get('default_width'))+'px';
|
||||
} else {
|
||||
width = "";
|
||||
}
|
||||
this.el.style.width = width;
|
||||
const flyout_el = this.el.querySelector('.box-flyout');
|
||||
if (!_.isNull(flyout_el)) {
|
||||
flyout_el.style.width = width;
|
||||
}
|
||||
},
|
||||
|
||||
adjustToViewport () {
|
||||
/* Event handler called when viewport gets resized. We remove
|
||||
* custom width/height from chat boxes.
|
||||
*/
|
||||
const viewport_width = Math.max(document.documentElement.clientWidth, window.innerWidth || 0);
|
||||
const viewport_height = Math.max(document.documentElement.clientHeight, window.innerHeight || 0);
|
||||
if (viewport_width <= 480) {
|
||||
this.model.set('height', undefined);
|
||||
this.model.set('width', undefined);
|
||||
} else if (viewport_width <= this.model.get('width')) {
|
||||
this.model.set('width', undefined);
|
||||
} else if (viewport_height <= this.model.get('height')) {
|
||||
this.model.set('height', undefined);
|
||||
}
|
||||
},
|
||||
|
||||
onStartVerticalResize (ev) {
|
||||
const { _converse } = this.__super__;
|
||||
if (!_converse.allow_dragresize) { return true; }
|
||||
// Record element attributes for mouseMove().
|
||||
const flyout = this.el.querySelector('.box-flyout'),
|
||||
style = window.getComputedStyle(flyout);
|
||||
this.height = parseInt(style.height.replace(/px$/, ''), 10);
|
||||
_converse.resizing = {
|
||||
'chatbox': this,
|
||||
'direction': 'top'
|
||||
};
|
||||
this.prev_pageY = ev.pageY;
|
||||
},
|
||||
|
||||
onStartHorizontalResize (ev) {
|
||||
const { _converse } = this.__super__;
|
||||
if (!_converse.allow_dragresize) { return true; }
|
||||
const flyout = this.el.querySelector('.box-flyout'),
|
||||
style = window.getComputedStyle(flyout);
|
||||
this.width = parseInt(style.width.replace(/px$/, ''), 10);
|
||||
_converse.resizing = {
|
||||
'chatbox': this,
|
||||
'direction': 'left'
|
||||
};
|
||||
this.prev_pageX = ev.pageX;
|
||||
},
|
||||
|
||||
onStartDiagonalResize (ev) {
|
||||
const { _converse } = this.__super__;
|
||||
this.onStartHorizontalResize(ev);
|
||||
this.onStartVerticalResize(ev);
|
||||
_converse.resizing.direction = 'topleft';
|
||||
},
|
||||
|
||||
resizeChatBox (ev) {
|
||||
let diff;
|
||||
const { _converse } = this.__super__;
|
||||
if (_converse.resizing.direction.indexOf('top') === 0) {
|
||||
diff = ev.pageY - this.prev_pageY;
|
||||
if (diff) {
|
||||
this.height = ((this.height-diff) > (this.model.get('min_height') || 0)) ? (this.height-diff) : this.model.get('min_height');
|
||||
this.prev_pageY = ev.pageY;
|
||||
this.setChatBoxHeight(this.height);
|
||||
}
|
||||
}
|
||||
if (_.includes(_converse.resizing.direction, 'left')) {
|
||||
diff = this.prev_pageX - ev.pageX;
|
||||
if (diff) {
|
||||
this.width = ((this.width+diff) > (this.model.get('min_width') || 0)) ? (this.width+diff) : this.model.get('min_width');
|
||||
this.prev_pageX = ev.pageX;
|
||||
this.setChatBoxWidth(this.width);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@ -270,11 +86,6 @@ converse.plugins.add('converse-dragresize', {
|
||||
'mousedown .dragresize-topleft': 'onStartDiagonalResize'
|
||||
},
|
||||
|
||||
initialize () {
|
||||
window.addEventListener('resize', _.debounce(this.setDimensions.bind(this), 100));
|
||||
return this.__super__.initialize.apply(this, arguments);
|
||||
},
|
||||
|
||||
render () {
|
||||
const result = this.__super__.render.apply(this, arguments);
|
||||
renderDragResizeHandles(this.__super__._converse, this);
|
||||
@ -290,11 +101,6 @@ converse.plugins.add('converse-dragresize', {
|
||||
'mousedown .dragresize-topleft': 'onStartDiagonalResize'
|
||||
},
|
||||
|
||||
initialize () {
|
||||
window.addEventListener('resize', _.debounce(this.setDimensions.bind(this), 100));
|
||||
this.__super__.initialize.apply(this, arguments);
|
||||
},
|
||||
|
||||
render () {
|
||||
const result = this.__super__.render.apply(this, arguments);
|
||||
renderDragResizeHandles(this.__super__._converse, this);
|
||||
@ -322,11 +128,6 @@ converse.plugins.add('converse-dragresize', {
|
||||
'mousedown .dragresize-topleft': 'onStartDiagonalResize'
|
||||
},
|
||||
|
||||
initialize () {
|
||||
window.addEventListener('resize', _.debounce(this.setDimensions.bind(this), 100));
|
||||
this.__super__.initialize.apply(this, arguments);
|
||||
},
|
||||
|
||||
render () {
|
||||
const result = this.__super__.render.apply(this, arguments);
|
||||
renderDragResizeHandles(this.__super__._converse, this);
|
||||
@ -343,9 +144,151 @@ converse.plugins.add('converse-dragresize', {
|
||||
const { _converse } = this;
|
||||
|
||||
_converse.api.settings.update({
|
||||
allow_dragresize: true,
|
||||
'allow_dragresize': true,
|
||||
});
|
||||
|
||||
|
||||
const dragResizable = {
|
||||
|
||||
initDragResize () {
|
||||
/* Determine and store the default box size.
|
||||
* We need this information for the drag-resizing feature.
|
||||
*/
|
||||
const flyout = this.el.querySelector('.box-flyout'),
|
||||
style = window.getComputedStyle(flyout);
|
||||
|
||||
if (_.isUndefined(this.model.get('height'))) {
|
||||
const height = parseInt(style.height.replace(/px$/, ''), 10),
|
||||
width = parseInt(style.width.replace(/px$/, ''), 10);
|
||||
this.model.set('height', height);
|
||||
this.model.set('default_height', height);
|
||||
this.model.set('width', width);
|
||||
this.model.set('default_width', width);
|
||||
}
|
||||
const min_width = style['min-width'];
|
||||
const min_height = style['min-height'];
|
||||
this.model.set('min_width', min_width.endsWith('px') ? Number(min_width.replace(/px$/, '')) :0);
|
||||
this.model.set('min_height', min_height.endsWith('px') ? Number(min_height.replace(/px$/, '')) :0);
|
||||
// Initialize last known mouse position
|
||||
this.prev_pageY = 0;
|
||||
this.prev_pageX = 0;
|
||||
if (_converse.connection.connected) {
|
||||
this.height = this.model.get('height');
|
||||
this.width = this.model.get('width');
|
||||
}
|
||||
return this;
|
||||
},
|
||||
|
||||
resizeChatBox (ev) {
|
||||
let diff;
|
||||
if (_converse.resizing.direction.indexOf('top') === 0) {
|
||||
diff = ev.pageY - this.prev_pageY;
|
||||
if (diff) {
|
||||
this.height = ((this.height-diff) > (this.model.get('min_height') || 0)) ? (this.height-diff) : this.model.get('min_height');
|
||||
this.prev_pageY = ev.pageY;
|
||||
this.setChatBoxHeight(this.height);
|
||||
}
|
||||
}
|
||||
if (_.includes(_converse.resizing.direction, 'left')) {
|
||||
diff = this.prev_pageX - ev.pageX;
|
||||
if (diff) {
|
||||
this.width = ((this.width+diff) > (this.model.get('min_width') || 0)) ? (this.width+diff) : this.model.get('min_width');
|
||||
this.prev_pageX = ev.pageX;
|
||||
this.setChatBoxWidth(this.width);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
setWidth () {
|
||||
// If a custom width is applied (due to drag-resizing),
|
||||
// then we need to set the width of the .chatbox element as well.
|
||||
if (this.model.get('width')) {
|
||||
this.el.style.width = this.model.get('width');
|
||||
}
|
||||
},
|
||||
|
||||
setDimensions () {
|
||||
// Make sure the chat box has the right height and width.
|
||||
this.adjustToViewport();
|
||||
this.setChatBoxHeight(this.model.get('height'));
|
||||
this.setChatBoxWidth(this.model.get('width'));
|
||||
},
|
||||
|
||||
setChatBoxHeight (height) {
|
||||
if (height) {
|
||||
height = _converse.applyDragResistance(height, this.model.get('default_height'))+'px';
|
||||
} else {
|
||||
height = "";
|
||||
}
|
||||
const flyout_el = this.el.querySelector('.box-flyout');
|
||||
if (!_.isNull(flyout_el)) {
|
||||
flyout_el.style.height = height;
|
||||
}
|
||||
},
|
||||
|
||||
setChatBoxWidth (width) {
|
||||
if (width) {
|
||||
width = _converse.applyDragResistance(width, this.model.get('default_width'))+'px';
|
||||
} else {
|
||||
width = "";
|
||||
}
|
||||
this.el.style.width = width;
|
||||
const flyout_el = this.el.querySelector('.box-flyout');
|
||||
if (!_.isNull(flyout_el)) {
|
||||
flyout_el.style.width = width;
|
||||
}
|
||||
},
|
||||
|
||||
adjustToViewport () {
|
||||
/* Event handler called when viewport gets resized. We remove
|
||||
* custom width/height from chat boxes.
|
||||
*/
|
||||
const viewport_width = Math.max(document.documentElement.clientWidth, window.innerWidth || 0);
|
||||
const viewport_height = Math.max(document.documentElement.clientHeight, window.innerHeight || 0);
|
||||
if (viewport_width <= 480) {
|
||||
this.model.set('height', undefined);
|
||||
this.model.set('width', undefined);
|
||||
} else if (viewport_width <= this.model.get('width')) {
|
||||
this.model.set('width', undefined);
|
||||
} else if (viewport_height <= this.model.get('height')) {
|
||||
this.model.set('height', undefined);
|
||||
}
|
||||
},
|
||||
|
||||
onStartVerticalResize (ev) {
|
||||
if (!_converse.allow_dragresize) { return true; }
|
||||
// Record element attributes for mouseMove().
|
||||
const flyout = this.el.querySelector('.box-flyout'),
|
||||
style = window.getComputedStyle(flyout);
|
||||
this.height = parseInt(style.height.replace(/px$/, ''), 10);
|
||||
_converse.resizing = {
|
||||
'chatbox': this,
|
||||
'direction': 'top'
|
||||
};
|
||||
this.prev_pageY = ev.pageY;
|
||||
},
|
||||
|
||||
onStartHorizontalResize (ev) {
|
||||
if (!_converse.allow_dragresize) { return true; }
|
||||
const flyout = this.el.querySelector('.box-flyout'),
|
||||
style = window.getComputedStyle(flyout);
|
||||
this.width = parseInt(style.width.replace(/px$/, ''), 10);
|
||||
_converse.resizing = {
|
||||
'chatbox': this,
|
||||
'direction': 'left'
|
||||
};
|
||||
this.prev_pageX = ev.pageX;
|
||||
},
|
||||
|
||||
onStartDiagonalResize (ev) {
|
||||
this.onStartHorizontalResize(ev);
|
||||
this.onStartVerticalResize(ev);
|
||||
_converse.resizing.direction = 'topleft';
|
||||
},
|
||||
};
|
||||
Object.assign(_converse.ChatBoxView.prototype, dragResizable);
|
||||
|
||||
|
||||
_converse.applyDragResistance = function (value, default_value) {
|
||||
/* This method applies some resistance around the
|
||||
* default_value. If value is close enough to
|
||||
@ -363,6 +306,45 @@ converse.plugins.add('converse-dragresize', {
|
||||
}
|
||||
return value;
|
||||
};
|
||||
|
||||
|
||||
/************************ BEGIN Event Handlers ************************/
|
||||
function registerGlobalEventHandlers () {
|
||||
|
||||
document.addEventListener('mousemove', function (ev) {
|
||||
if (!_converse.resizing || !_converse.allow_dragresize) { return true; }
|
||||
ev.preventDefault();
|
||||
_converse.resizing.chatbox.resizeChatBox(ev);
|
||||
});
|
||||
|
||||
document.addEventListener('mouseup', function (ev) {
|
||||
if (!_converse.resizing || !_converse.allow_dragresize) { return true; }
|
||||
ev.preventDefault();
|
||||
const height = _converse.applyDragResistance(
|
||||
_converse.resizing.chatbox.height,
|
||||
_converse.resizing.chatbox.model.get('default_height')
|
||||
);
|
||||
const width = _converse.applyDragResistance(
|
||||
_converse.resizing.chatbox.width,
|
||||
_converse.resizing.chatbox.model.get('default_width')
|
||||
);
|
||||
if (_converse.connection.connected) {
|
||||
_converse.resizing.chatbox.model.save({'height': height});
|
||||
_converse.resizing.chatbox.model.save({'width': width});
|
||||
} else {
|
||||
_converse.resizing.chatbox.model.set({'height': height});
|
||||
_converse.resizing.chatbox.model.set({'width': width});
|
||||
}
|
||||
_converse.resizing = null;
|
||||
});
|
||||
}
|
||||
_converse.api.listen.on('registeredGlobalEventHandlers', registerGlobalEventHandlers);
|
||||
|
||||
|
||||
_converse.api.listen.on('chatBoxInitialized', view => {
|
||||
window.addEventListener('resize', _.debounce(() => view.setDimensions(), 100));
|
||||
});
|
||||
/************************ END Event Handlers ************************/
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -30,7 +30,7 @@ converse.plugins.add('converse-minimize', {
|
||||
*
|
||||
* NB: These plugins need to have already been loaded via require.js.
|
||||
*/
|
||||
dependencies: ["converse-chatview", "converse-controlbox", "converse-muc", "converse-muc-views", "converse-headline"],
|
||||
dependencies: ["converse-chatview", "converse-controlbox", "converse-muc-views", "converse-headline", "converse-dragresize"],
|
||||
|
||||
enabled (_converse) {
|
||||
return _converse.view_mode === 'overlayed';
|
||||
@ -57,20 +57,6 @@ converse.plugins.add('converse-minimize', {
|
||||
});
|
||||
},
|
||||
|
||||
maximize () {
|
||||
u.safeSave(this, {
|
||||
'minimized': false,
|
||||
'time_opened': (new Date()).getTime()
|
||||
});
|
||||
},
|
||||
|
||||
minimize () {
|
||||
u.safeSave(this, {
|
||||
'minimized': true,
|
||||
'time_minimized': (new Date()).toISOString()
|
||||
});
|
||||
},
|
||||
|
||||
maybeShow (force) {
|
||||
if (!force && this.get('minimized')) {
|
||||
// Must return the chatbox
|
||||
@ -122,65 +108,17 @@ converse.plugins.add('converse-minimize', {
|
||||
if (!this.model.get('minimized')) {
|
||||
return this.__super__.setChatBoxWidth.apply(this, arguments);
|
||||
}
|
||||
},
|
||||
|
||||
onMinimizedChanged (item) {
|
||||
if (item.get('minimized')) {
|
||||
this.minimize();
|
||||
} else {
|
||||
this.maximize();
|
||||
}
|
||||
},
|
||||
|
||||
maximize () {
|
||||
// Restores a minimized chat box
|
||||
const { _converse } = this.__super__;
|
||||
this.insertIntoDOM();
|
||||
|
||||
if (!this.model.isScrolledUp()) {
|
||||
this.model.clearUnreadMsgCounter();
|
||||
}
|
||||
this.show();
|
||||
/**
|
||||
* Triggered when a previously minimized chat gets maximized
|
||||
* @event _converse#chatBoxMaximized
|
||||
* @type { _converse.ChatBoxView }
|
||||
* @example _converse.api.listen.on('chatBoxMaximized', view => { ... });
|
||||
*/
|
||||
_converse.api.trigger('chatBoxMaximized', this);
|
||||
return this;
|
||||
},
|
||||
|
||||
minimize (ev) {
|
||||
const { _converse } = this.__super__;
|
||||
if (ev && ev.preventDefault) { ev.preventDefault(); }
|
||||
// save the scroll position to restore it on maximize
|
||||
if (this.model.collection && this.model.collection.browserStorage) {
|
||||
this.model.save({'scroll': this.content.scrollTop});
|
||||
} else {
|
||||
this.model.set({'scroll': this.content.scrollTop});
|
||||
}
|
||||
this.setChatState(_converse.INACTIVE).model.minimize();
|
||||
this.hide();
|
||||
/**
|
||||
* Triggered when a previously maximized chat gets Minimized
|
||||
* @event _converse#chatBoxMinimized
|
||||
* @type { _converse.ChatBoxView }
|
||||
* @example _converse.api.listen.on('chatBoxMinimized', view => { ... });
|
||||
*/
|
||||
_converse.api.trigger('chatBoxMinimized', this);
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
ChatBoxHeading: {
|
||||
|
||||
render () {
|
||||
const { _converse } = this.__super__,
|
||||
{ __ } = _converse;
|
||||
const result = this.__super__.render.apply(this, arguments);
|
||||
const new_html = tpl_chatbox_minimize(
|
||||
{info_minimize: __('Minimize this chat box')}
|
||||
);
|
||||
const new_html = tpl_chatbox_minimize({
|
||||
'info_minimize': __('Minimize this chat box')
|
||||
});
|
||||
const el = this.el.querySelector('.toggle-chatbox-button');
|
||||
if (el) {
|
||||
el.outerHTML = new_html;
|
||||
@ -227,11 +165,109 @@ converse.plugins.add('converse-minimize', {
|
||||
}
|
||||
return div.innerHTML;
|
||||
}
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
ChatBoxViews: {
|
||||
|
||||
initialize () {
|
||||
/* The initialize function gets called as soon as the plugin is
|
||||
* loaded by Converse.js's plugin machinery.
|
||||
*/
|
||||
const { _converse } = this,
|
||||
{ __ } = _converse;
|
||||
|
||||
// Add new HTML templates.
|
||||
_converse.templates.chatbox_minimize = tpl_chatbox_minimize;
|
||||
_converse.templates.toggle_chats = tpl_toggle_chats;
|
||||
_converse.templates.trimmed_chat = tpl_trimmed_chat;
|
||||
_converse.templates.chats_panel = tpl_chats_panel;
|
||||
|
||||
_converse.api.settings.update({
|
||||
'no_trimming': false, // Set to true for tests
|
||||
});
|
||||
|
||||
|
||||
const minimizableChatBox = {
|
||||
maximize () {
|
||||
u.safeSave(this, {
|
||||
'minimized': false,
|
||||
'time_opened': (new Date()).getTime()
|
||||
});
|
||||
},
|
||||
|
||||
minimize () {
|
||||
u.safeSave(this, {
|
||||
'minimized': true,
|
||||
'time_minimized': (new Date()).toISOString()
|
||||
});
|
||||
}
|
||||
}
|
||||
Object.assign(_converse.ChatBox.prototype, minimizableChatBox);
|
||||
|
||||
|
||||
const minimizableChatBoxView = {
|
||||
maximize () {
|
||||
// Restores a minimized chat box
|
||||
const { _converse } = this.__super__;
|
||||
this.insertIntoDOM();
|
||||
|
||||
if (!this.model.isScrolledUp()) {
|
||||
this.model.clearUnreadMsgCounter();
|
||||
}
|
||||
this.show();
|
||||
/**
|
||||
* Triggered when a previously minimized chat gets maximized
|
||||
* @event _converse#chatBoxMaximized
|
||||
* @type { _converse.ChatBoxView }
|
||||
* @example _converse.api.listen.on('chatBoxMaximized', view => { ... });
|
||||
*/
|
||||
_converse.api.trigger('chatBoxMaximized', this);
|
||||
return this;
|
||||
},
|
||||
|
||||
minimize (ev) {
|
||||
const { _converse } = this.__super__;
|
||||
if (ev && ev.preventDefault) { ev.preventDefault(); }
|
||||
// save the scroll position to restore it on maximize
|
||||
if (this.model.collection && this.model.collection.browserStorage) {
|
||||
this.model.save({'scroll': this.content.scrollTop});
|
||||
} else {
|
||||
this.model.set({'scroll': this.content.scrollTop});
|
||||
}
|
||||
this.setChatState(_converse.INACTIVE).model.minimize();
|
||||
this.hide();
|
||||
/**
|
||||
* Triggered when a previously maximized chat gets Minimized
|
||||
* @event _converse#chatBoxMinimized
|
||||
* @type { _converse.ChatBoxView }
|
||||
* @example _converse.api.listen.on('chatBoxMinimized', view => { ... });
|
||||
*/
|
||||
_converse.api.trigger('chatBoxMinimized', this);
|
||||
},
|
||||
|
||||
onMinimizedChanged (item) {
|
||||
if (item.get('minimized')) {
|
||||
this.minimize();
|
||||
} else {
|
||||
this.maximize();
|
||||
}
|
||||
}
|
||||
}
|
||||
Object.assign(_converse.ChatBoxView.prototype, minimizableChatBoxView);
|
||||
|
||||
|
||||
const chatTrimmer = {
|
||||
getChatBoxWidth (view) {
|
||||
if (!view.model.get('minimized') && u.isVisible(view.el)) {
|
||||
if (view.model.get('id') === 'controlbox') {
|
||||
const controlbox = this.get('controlbox');
|
||||
// We return the width of the controlbox or its toggle,
|
||||
// depending on which is visible.
|
||||
if (!controlbox || !u.isVisible(controlbox.el)) {
|
||||
return u.getOuterWidth(_converse.controlboxtoggle.el, true);
|
||||
} else {
|
||||
return u.getOuterWidth(controlbox.el, true);
|
||||
}
|
||||
} else if (!view.model.get('minimized') && u.isVisible(view.el)) {
|
||||
return u.getOuterWidth(view.el, true);
|
||||
}
|
||||
return 0;
|
||||
@ -315,26 +351,9 @@ converse.plugins.add('converse-minimize', {
|
||||
return model;
|
||||
}
|
||||
}
|
||||
},
|
||||
Object.assign(_converse.ChatBoxViews.prototype, chatTrimmer);
|
||||
|
||||
|
||||
initialize () {
|
||||
/* The initialize function gets called as soon as the plugin is
|
||||
* loaded by Converse.js's plugin machinery.
|
||||
*/
|
||||
const { _converse } = this,
|
||||
{ __ } = _converse;
|
||||
|
||||
// Add new HTML templates.
|
||||
_converse.templates.chatbox_minimize = tpl_chatbox_minimize;
|
||||
_converse.templates.toggle_chats = tpl_toggle_chats;
|
||||
_converse.templates.trimmed_chat = tpl_trimmed_chat;
|
||||
_converse.templates.chats_panel = tpl_chats_panel;
|
||||
|
||||
_converse.api.settings.update({
|
||||
no_trimming: false, // Set to true for phantomjs tests (where browser apparently has no width)
|
||||
});
|
||||
|
||||
_converse.api.promises.add('minimizedChatsInitialized');
|
||||
|
||||
_converse.MinimizedChatBoxView = Backbone.NativeView.extend({
|
||||
@ -525,6 +544,7 @@ converse.plugins.add('converse-minimize', {
|
||||
}
|
||||
});
|
||||
|
||||
/************************ BEGIN Event Handlers ************************/
|
||||
Promise.all([
|
||||
_converse.api.waitUntil('connectionInitialized'),
|
||||
_converse.api.waitUntil('chatBoxViewsInitialized')
|
||||
@ -559,5 +579,6 @@ converse.plugins.add('converse-minimize', {
|
||||
_converse.chatboxviews.trimChats(chatbox);
|
||||
}
|
||||
});
|
||||
/************************ END Event Handlers ************************/
|
||||
}
|
||||
});
|
||||
|
@ -62,41 +62,14 @@ converse.plugins.add('converse-muc-views', {
|
||||
dependencies: ["converse-autocomplete", "converse-modal", "converse-controlbox", "converse-chatview"],
|
||||
|
||||
overrides: {
|
||||
|
||||
ControlBoxView: {
|
||||
|
||||
renderRoomsPanel () {
|
||||
const { _converse } = this.__super__;
|
||||
if (this.roomspanel && u.isVisible(this.roomspanel.el)) {
|
||||
return;
|
||||
}
|
||||
this.roomspanel = new _converse.RoomsPanel({
|
||||
'model': new (_converse.RoomsPanelModel.extend({
|
||||
'id': `converse.roomspanel${_converse.bare_jid}`, // Required by web storage
|
||||
'browserStorage': new BrowserStorage[_converse.config.get('storage')](
|
||||
`converse.roomspanel${_converse.bare_jid}`)
|
||||
}))()
|
||||
});
|
||||
this.roomspanel.model.fetch();
|
||||
this.el.querySelector('.controlbox-pane').insertAdjacentElement(
|
||||
'beforeEnd', this.roomspanel.render().el);
|
||||
|
||||
/**
|
||||
* Triggered once the section of the _converse.ControlBoxView
|
||||
* which shows gropuchats has been rendered.
|
||||
* @event _converse#roomsPanelRendered
|
||||
* @example _converse.api.listen.on('roomsPanelRendered', () => { ... });
|
||||
*/
|
||||
_converse.api.trigger('roomsPanelRendered');
|
||||
},
|
||||
|
||||
renderControlBoxPane () {
|
||||
const { _converse } = this.__super__;
|
||||
this.__super__.renderControlBoxPane.apply(this, arguments);
|
||||
if (_converse.allow_muc) {
|
||||
this.renderRoomsPanel();
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@ -123,6 +96,35 @@ converse.plugins.add('converse-muc-views', {
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
Object.assign(_converse.ControlBoxView.prototype, {
|
||||
|
||||
renderRoomsPanel () {
|
||||
if (this.roomspanel && u.isVisible(this.roomspanel.el)) {
|
||||
return;
|
||||
}
|
||||
this.roomspanel = new _converse.RoomsPanel({
|
||||
'model': new (_converse.RoomsPanelModel.extend({
|
||||
'id': `converse.roomspanel${_converse.bare_jid}`, // Required by web storage
|
||||
'browserStorage': new BrowserStorage[_converse.config.get('storage')](
|
||||
`converse.roomspanel${_converse.bare_jid}`)
|
||||
}))()
|
||||
});
|
||||
this.roomspanel.model.fetch();
|
||||
this.el.querySelector('.controlbox-pane').insertAdjacentElement(
|
||||
'beforeEnd', this.roomspanel.render().el);
|
||||
|
||||
/**
|
||||
* Triggered once the section of the _converse.ControlBoxView
|
||||
* which shows gropuchats has been rendered.
|
||||
* @event _converse#roomsPanelRendered
|
||||
* @example _converse.api.listen.on('roomsPanelRendered', () => { ... });
|
||||
*/
|
||||
_converse.api.trigger('roomsPanelRendered');
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
function ___ (str) {
|
||||
/* This is part of a hack to get gettext to scan strings to be
|
||||
* translated. Strings we cannot send to the function above because
|
||||
@ -572,6 +574,7 @@ converse.plugins.add('converse-muc-views', {
|
||||
* @example _converse.api.listen.on('chatRoomOpened', view => { ... });
|
||||
*/
|
||||
_converse.api.trigger('chatRoomOpened', this);
|
||||
_converse.api.trigger('chatBoxInitialized', this);
|
||||
},
|
||||
|
||||
render () {
|
||||
|
@ -38,12 +38,12 @@ converse.plugins.add("converse-oauth", {
|
||||
/* For example, the private *_converse* object has a
|
||||
* method "onConnected". You can override that method as follows:
|
||||
*/
|
||||
'LoginPanel': {
|
||||
LoginPanel: {
|
||||
|
||||
insertOAuthProviders () {
|
||||
const { _converse } = this.__super__;
|
||||
if (_.isUndefined(this.oauth_providers_view)) {
|
||||
this.oauth_providers_view =
|
||||
this.oauth_providers_view =
|
||||
new _converse.OAuthProvidersView({'model': _converse.oauth_providers});
|
||||
|
||||
this.oauth_providers_view.render();
|
||||
|
@ -172,6 +172,90 @@ converse.plugins.add('converse-omemo', {
|
||||
},
|
||||
|
||||
ChatBox: {
|
||||
async getMessageAttributesFromStanza (stanza, original_stanza) {
|
||||
const { _converse } = this.__super__;
|
||||
const encrypted = sizzle(`encrypted[xmlns="${Strophe.NS.OMEMO}"]`, original_stanza).pop(),
|
||||
attrs = await this.__super__.getMessageAttributesFromStanza.apply(this, arguments);
|
||||
|
||||
if (!encrypted || !_converse.config.get('trusted')) {
|
||||
return attrs;
|
||||
} else {
|
||||
return this.getEncryptionAttributesfromStanza(stanza, original_stanza, attrs);
|
||||
}
|
||||
},
|
||||
|
||||
async sendMessage (text, spoiler_hint) {
|
||||
if (this.get('omemo_active') && text) {
|
||||
const { _converse } = this.__super__;
|
||||
const attrs = this.getOutgoingMessageAttributes(text, spoiler_hint);
|
||||
attrs['is_encrypted'] = true;
|
||||
attrs['plaintext'] = attrs.message;
|
||||
try {
|
||||
const devices = await _converse.getBundlesAndBuildSessions(this);
|
||||
const stanza = await _converse.createOMEMOMessageStanza(this, this.messages.create(attrs), devices);
|
||||
_converse.api.send(stanza);
|
||||
} catch (e) {
|
||||
this.handleMessageSendError(e);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
return this.__super__.sendMessage.apply(this, arguments);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
ChatBoxView: {
|
||||
events: {
|
||||
'click .toggle-omemo': 'toggleOMEMO'
|
||||
},
|
||||
|
||||
initialize () {
|
||||
this.__super__.initialize.apply(this, arguments);
|
||||
this.model.on('change:omemo_active', this.renderOMEMOToolbarButton, this);
|
||||
this.model.on('change:omemo_supported', this.onOMEMOSupportedDetermined, this);
|
||||
},
|
||||
|
||||
showMessage (message) {
|
||||
// We don't show a message if it's only keying material
|
||||
if (!message.get('is_only_key')) {
|
||||
return this.__super__.showMessage.apply(this, arguments);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
ChatRoomView: {
|
||||
events: {
|
||||
'click .toggle-omemo': 'toggleOMEMO'
|
||||
},
|
||||
|
||||
initialize () {
|
||||
this.__super__.initialize.apply(this, arguments);
|
||||
this.model.on('change:omemo_active', this.renderOMEMOToolbarButton, this);
|
||||
this.model.on('change:omemo_supported', this.onOMEMOSupportedDetermined, this);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
initialize () {
|
||||
/* The initialize function gets called as soon as the plugin is
|
||||
* loaded by Converse.js's plugin machinery.
|
||||
*/
|
||||
const { _converse } = this,
|
||||
{ __ } = _converse;
|
||||
|
||||
_converse.api.promises.add(['OMEMOInitialized']);
|
||||
|
||||
_converse.NUM_PREKEYS = 100; // Set here so that tests can override
|
||||
|
||||
|
||||
/**
|
||||
* Mixin object that contains OMEMO-related methods for
|
||||
* {@link _converse.ChatBox} or {@link _converse.ChatRoom} objects.
|
||||
*
|
||||
* @typedef {Object} OMEMOEnabledChatBox
|
||||
*/
|
||||
const OMEMOEnabledChatBox = {
|
||||
|
||||
async encryptMessage (plaintext) {
|
||||
// The client MUST use fresh, randomly generated key/IV pairs
|
||||
@ -219,7 +303,6 @@ converse.plugins.add('converse-omemo', {
|
||||
},
|
||||
|
||||
reportDecryptionError (e) {
|
||||
const { _converse } = this.__super__;
|
||||
if (_converse.debug) {
|
||||
const { __ } = _converse;
|
||||
this.messages.create({
|
||||
@ -231,8 +314,7 @@ converse.plugins.add('converse-omemo', {
|
||||
},
|
||||
|
||||
async handleDecryptedWhisperMessage (attrs, key_and_tag) {
|
||||
const { _converse } = this.__super__,
|
||||
encrypted = attrs.encrypted,
|
||||
const encrypted = attrs.encrypted,
|
||||
devicelist = _converse.devicelists.getDeviceList(this.get('jid'));
|
||||
|
||||
this.save('omemo_supported', true);
|
||||
@ -250,8 +332,7 @@ converse.plugins.add('converse-omemo', {
|
||||
},
|
||||
|
||||
decrypt (attrs) {
|
||||
const { _converse } = this.__super__,
|
||||
session_cipher = this.getSessionCipher(attrs.from, parseInt(attrs.encrypted.device_id, 10));
|
||||
const session_cipher = this.getSessionCipher(attrs.from, parseInt(attrs.encrypted.device_id, 10));
|
||||
|
||||
// https://xmpp.org/extensions/xep-0384.html#usecases-receiving
|
||||
if (attrs.encrypted.prekey === true) {
|
||||
@ -284,8 +365,7 @@ converse.plugins.add('converse-omemo', {
|
||||
},
|
||||
|
||||
getEncryptionAttributesfromStanza (stanza, original_stanza, attrs) {
|
||||
const { _converse } = this.__super__,
|
||||
encrypted = sizzle(`encrypted[xmlns="${Strophe.NS.OMEMO}"]`, original_stanza).pop(),
|
||||
const encrypted = sizzle(`encrypted[xmlns="${Strophe.NS.OMEMO}"]`, original_stanza).pop(),
|
||||
header = encrypted.querySelector('header'),
|
||||
key = sizzle(`key[rid="${_converse.omemo_store.get('device_id')}"]`, encrypted).pop();
|
||||
if (key) {
|
||||
@ -303,22 +383,8 @@ converse.plugins.add('converse-omemo', {
|
||||
}
|
||||
},
|
||||
|
||||
async getMessageAttributesFromStanza (stanza, original_stanza) {
|
||||
const { _converse } = this.__super__,
|
||||
encrypted = sizzle(`encrypted[xmlns="${Strophe.NS.OMEMO}"]`, original_stanza).pop(),
|
||||
attrs = await this.__super__.getMessageAttributesFromStanza.apply(this, arguments);
|
||||
|
||||
if (!encrypted || !_converse.config.get('trusted')) {
|
||||
return attrs;
|
||||
} else {
|
||||
return this.getEncryptionAttributesfromStanza(stanza, original_stanza, attrs);
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
getSessionCipher (jid, id) {
|
||||
const { _converse } = this.__super__,
|
||||
address = new libsignal.SignalProtocolAddress(jid, id);
|
||||
const address = new libsignal.SignalProtocolAddress(jid, id);
|
||||
this.session_cipher = new window.libsignal.SessionCipher(_converse.omemo_store, address);
|
||||
return this.session_cipher;
|
||||
},
|
||||
@ -330,8 +396,6 @@ converse.plugins.add('converse-omemo', {
|
||||
},
|
||||
|
||||
handleMessageSendError (e) {
|
||||
const { _converse } = this.__super__,
|
||||
{ __ } = _converse;
|
||||
if (e.name === 'IQError') {
|
||||
this.save('omemo_supported', false);
|
||||
|
||||
@ -355,46 +419,12 @@ converse.plugins.add('converse-omemo', {
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
},
|
||||
|
||||
async sendMessage (text, spoiler_hint) {
|
||||
if (this.get('omemo_active') && text) {
|
||||
const { _converse } = this.__super__;
|
||||
const attrs = this.getOutgoingMessageAttributes(text, spoiler_hint);
|
||||
attrs['is_encrypted'] = true;
|
||||
attrs['plaintext'] = attrs.message;
|
||||
try {
|
||||
const devices = await _converse.getBundlesAndBuildSessions(this);
|
||||
const stanza = await _converse.createOMEMOMessageStanza(this, this.messages.create(attrs), devices);
|
||||
_converse.api.send(stanza);
|
||||
} catch (e) {
|
||||
this.handleMessageSendError(e);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
return this.__super__.sendMessage.apply(this, arguments);
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
Object.assign(_converse.ChatBox.prototype, OMEMOEnabledChatBox);
|
||||
|
||||
ChatBoxView: {
|
||||
events: {
|
||||
'click .toggle-omemo': 'toggleOMEMO'
|
||||
},
|
||||
|
||||
initialize () {
|
||||
this.__super__.initialize.apply(this, arguments);
|
||||
this.model.on('change:omemo_active', this.renderOMEMOToolbarButton, this);
|
||||
this.model.on('change:omemo_supported', this.onOMEMOSupportedDetermined, this);
|
||||
},
|
||||
|
||||
showMessage (message) {
|
||||
// We don't show a message if it's only keying material
|
||||
if (!message.get('is_only_key')) {
|
||||
return this.__super__.showMessage.apply(this, arguments);
|
||||
}
|
||||
},
|
||||
const OMEMOEnabledChatView = {
|
||||
|
||||
onOMEMOSupportedDetermined () {
|
||||
if (!this.model.get('omemo_supported') && this.model.get('omemo_active')) {
|
||||
@ -405,82 +435,47 @@ converse.plugins.add('converse-omemo', {
|
||||
},
|
||||
|
||||
renderOMEMOToolbarButton () {
|
||||
const { _converse } = this.__super__,
|
||||
{ __ } = _converse,
|
||||
icon = this.el.querySelector('.toggle-omemo'),
|
||||
html = tpl_toolbar_omemo(Object.assign(this.model.toJSON(), {'__': __}));
|
||||
if (this.model.get('type') !== _converse.CHATROOMS_TYPE ||
|
||||
this.model.features.get('membersonly') &&
|
||||
this.model.features.get('nonanonymous')) {
|
||||
|
||||
if (icon) {
|
||||
icon.outerHTML = html;
|
||||
} else {
|
||||
this.el.querySelector('.chat-toolbar').insertAdjacentHTML('beforeend', html);
|
||||
}
|
||||
},
|
||||
|
||||
toggleOMEMO (ev) {
|
||||
const { _converse } = this.__super__, { __ } = _converse;
|
||||
if (!this.model.get('omemo_supported')) {
|
||||
return _converse.api.alert.show(
|
||||
Strophe.LogLevel.ERROR,
|
||||
__('Error'),
|
||||
[__("Cannot use end-to-end encryption because %1$s uses a client that doesn't support OMEMO.",
|
||||
this.model.contact.getDisplayName()
|
||||
)]
|
||||
)
|
||||
}
|
||||
ev.preventDefault();
|
||||
this.model.save({'omemo_active': !this.model.get('omemo_active')});
|
||||
}
|
||||
},
|
||||
|
||||
ChatRoomView: {
|
||||
events: {
|
||||
'click .toggle-omemo': 'toggleOMEMO'
|
||||
},
|
||||
|
||||
initialize () {
|
||||
this.__super__.initialize.apply(this, arguments);
|
||||
this.model.on('change:omemo_active', this.renderOMEMOToolbarButton, this);
|
||||
this.model.on('change:omemo_supported', this.onOMEMOSupportedDetermined, this);
|
||||
},
|
||||
|
||||
toggleOMEMO (ev) {
|
||||
const { _converse } = this.__super__, { __ } = _converse;
|
||||
if (!this.model.get('omemo_supported')) {
|
||||
return _converse.api.alert.show(
|
||||
Strophe.LogLevel.ERROR,
|
||||
__('Error'),
|
||||
[__('Cannot use end-to-end encryption in this groupchat, '+
|
||||
'either the groupchat has some anonymity or not all participants support OMEMO.')]
|
||||
);
|
||||
}
|
||||
ev.preventDefault();
|
||||
this.model.save({'omemo_active': !this.model.get('omemo_active')});
|
||||
},
|
||||
|
||||
renderOMEMOToolbarButton () {
|
||||
if (this.model.features.get('membersonly') && this.model.features.get('nonanonymous')) {
|
||||
this.__super__.renderOMEMOToolbarButton.apply(arguments);
|
||||
const icon = this.el.querySelector('.toggle-omemo');
|
||||
const html = tpl_toolbar_omemo(Object.assign(this.model.toJSON(), {'__': __}));
|
||||
if (icon) {
|
||||
icon.outerHTML = html;
|
||||
} else {
|
||||
this.el.querySelector('.chat-toolbar').insertAdjacentHTML('beforeend', html);
|
||||
}
|
||||
} else {
|
||||
const icon = this.el.querySelector('.toggle-omemo');
|
||||
if (icon) {
|
||||
icon.parentElement.removeChild(icon);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
toggleOMEMO (ev) {
|
||||
if (!this.model.get('omemo_supported')) {
|
||||
let messages;
|
||||
if (this.model.get('type') === _converse.CHATROOMS_TYPE) {
|
||||
messages = [__(
|
||||
'Cannot use end-to-end encryption in this groupchat, '+
|
||||
'either the groupchat has some anonymity or not all participants support OMEMO.'
|
||||
)];
|
||||
} else {
|
||||
messages = [__(
|
||||
"Cannot use end-to-end encryption because %1$s uses a client that doesn't support OMEMO.",
|
||||
this.model.contact.getDisplayName()
|
||||
)];
|
||||
}
|
||||
return _converse.api.alert.show(Strophe.LogLevel.ERROR, __('Error'), messages);
|
||||
}
|
||||
ev.preventDefault();
|
||||
this.model.save({'omemo_active': !this.model.get('omemo_active')});
|
||||
}
|
||||
}
|
||||
},
|
||||
Object.assign(_converse.ChatBoxView.prototype, OMEMOEnabledChatView);
|
||||
|
||||
initialize () {
|
||||
/* The initialize function gets called as soon as the plugin is
|
||||
* loaded by Converse.js's plugin machinery.
|
||||
*/
|
||||
const { _converse } = this,
|
||||
{ __ } = _converse;
|
||||
|
||||
_converse.api.promises.add(['OMEMOInitialized']);
|
||||
|
||||
_converse.NUM_PREKEYS = 100; // Set here so that tests can override
|
||||
|
||||
async function generateFingerprint (device) {
|
||||
if (_.get(device.get('bundle'), 'fingerprint')) {
|
||||
|
@ -45,17 +45,6 @@ converse.plugins.add('converse-register', {
|
||||
// New functions which don't exist yet can also be added.
|
||||
|
||||
LoginPanel: {
|
||||
|
||||
insertRegisterLink () {
|
||||
const { _converse } = this.__super__;
|
||||
if (_.isUndefined(this.registerlinkview)) {
|
||||
this.registerlinkview = new _converse.RegisterLinkView({'model': this.model});
|
||||
this.registerlinkview.render();
|
||||
this.el.querySelector('.buttons').insertAdjacentElement('afterend', this.registerlinkview.el);
|
||||
}
|
||||
this.registerlinkview.render();
|
||||
},
|
||||
|
||||
render (cfg) {
|
||||
const { _converse } = this.__super__;
|
||||
this.__super__.render.apply(this, arguments);
|
||||
@ -67,43 +56,6 @@ converse.plugins.add('converse-register', {
|
||||
},
|
||||
|
||||
ControlBoxView: {
|
||||
|
||||
initialize () {
|
||||
this.__super__.initialize.apply(this, arguments);
|
||||
this.model.on('change:active-form', this.showLoginOrRegisterForm.bind(this))
|
||||
},
|
||||
|
||||
showLoginOrRegisterForm () {
|
||||
const { _converse } = this.__super__;
|
||||
if (_.isNil(this.registerpanel)) {
|
||||
return;
|
||||
}
|
||||
if (this.model.get('active-form') == "register") {
|
||||
this.loginpanel.el.classList.add('hidden');
|
||||
this.registerpanel.el.classList.remove('hidden');
|
||||
} else {
|
||||
this.loginpanel.el.classList.remove('hidden');
|
||||
this.registerpanel.el.classList.add('hidden');
|
||||
}
|
||||
},
|
||||
|
||||
renderRegistrationPanel () {
|
||||
const { _converse } = this.__super__;
|
||||
if (_converse.allow_registration) {
|
||||
this.registerpanel = new _converse.RegisterPanel({
|
||||
'model': this.model
|
||||
});
|
||||
this.registerpanel.render();
|
||||
this.registerpanel.el.classList.add('hidden');
|
||||
this.el.querySelector('#converse-login-panel').insertAdjacentElement(
|
||||
'afterend',
|
||||
this.registerpanel.el
|
||||
);
|
||||
this.showLoginOrRegisterForm();
|
||||
}
|
||||
return this;
|
||||
},
|
||||
|
||||
renderLoginPanel () {
|
||||
/* Also render a registration panel, when rendering the
|
||||
* login panel.
|
||||
@ -135,6 +87,52 @@ converse.plugins.add('converse-register', {
|
||||
});
|
||||
|
||||
|
||||
Object.assign(_converse.LoginPanel.prototype, {
|
||||
|
||||
insertRegisterLink () {
|
||||
if (_.isUndefined(this.registerlinkview)) {
|
||||
this.registerlinkview = new _converse.RegisterLinkView({'model': this.model});
|
||||
this.registerlinkview.render();
|
||||
this.el.querySelector('.buttons').insertAdjacentElement('afterend', this.registerlinkview.el);
|
||||
}
|
||||
this.registerlinkview.render();
|
||||
}
|
||||
});
|
||||
|
||||
Object.assign(_converse.ControlBoxView.prototype, {
|
||||
|
||||
showLoginOrRegisterForm () {
|
||||
const { _converse } = this.__super__;
|
||||
if (_.isNil(this.registerpanel)) {
|
||||
return;
|
||||
}
|
||||
if (this.model.get('active-form') == "register") {
|
||||
this.loginpanel.el.classList.add('hidden');
|
||||
this.registerpanel.el.classList.remove('hidden');
|
||||
} else {
|
||||
this.loginpanel.el.classList.remove('hidden');
|
||||
this.registerpanel.el.classList.add('hidden');
|
||||
}
|
||||
},
|
||||
|
||||
renderRegistrationPanel () {
|
||||
if (_converse.allow_registration) {
|
||||
this.registerpanel = new _converse.RegisterPanel({
|
||||
'model': this.model
|
||||
});
|
||||
this.registerpanel.render();
|
||||
this.registerpanel.el.classList.add('hidden');
|
||||
this.el.querySelector('#converse-login-panel').insertAdjacentElement(
|
||||
'afterend',
|
||||
this.registerpanel.el
|
||||
);
|
||||
this.showLoginOrRegisterForm();
|
||||
}
|
||||
return this;
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
function setActiveForm (value) {
|
||||
_converse.api.waitUntil('controlboxInitialized').then(() => {
|
||||
const controlbox = _converse.chatboxes.get('controlbox')
|
||||
@ -679,6 +677,12 @@ converse.plugins.add('converse-register', {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
/************************ BEGIN Event Handlers ************************/
|
||||
_converse.api.listen.on('controlboxInitialized', view => {
|
||||
view.model.on('change:active-form', view.showLoginOrRegisterForm, view);
|
||||
});
|
||||
/************************ END Event Handlers ************************/
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -29,27 +29,6 @@ converse.plugins.add('converse-rosterview', {
|
||||
|
||||
dependencies: ["converse-roster", "converse-modal"],
|
||||
|
||||
overrides: {
|
||||
// Overrides mentioned here will be picked up by converse.js's
|
||||
// plugin architecture they will replace existing methods on the
|
||||
// relevant objects or classes.
|
||||
//
|
||||
// New functions which don't exist yet can also be added.
|
||||
afterReconnected () {
|
||||
this.__super__.afterReconnected.apply(this, arguments);
|
||||
},
|
||||
|
||||
RosterGroups: {
|
||||
comparator () {
|
||||
// RosterGroupsComparator only gets set later (once i18n is
|
||||
// set up), so we need to wrap it in this nameless function.
|
||||
const { _converse } = this.__super__;
|
||||
return _converse.RosterGroupsComparator.apply(this, arguments);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
initialize () {
|
||||
/* The initialize function gets called as soon as the plugin is
|
||||
* loaded by converse.js's plugin machinery.
|
||||
@ -78,36 +57,6 @@ converse.plugins.add('converse-rosterview', {
|
||||
'away': __('This contact is away')
|
||||
};
|
||||
const LABEL_GROUPS = __('Groups');
|
||||
const HEADER_CURRENT_CONTACTS = __('My contacts');
|
||||
const HEADER_PENDING_CONTACTS = __('Pending contacts');
|
||||
const HEADER_REQUESTING_CONTACTS = __('Contact requests');
|
||||
const HEADER_UNGROUPED = __('Ungrouped');
|
||||
const HEADER_WEIGHTS = {};
|
||||
HEADER_WEIGHTS[HEADER_REQUESTING_CONTACTS] = 0;
|
||||
HEADER_WEIGHTS[HEADER_CURRENT_CONTACTS] = 1;
|
||||
HEADER_WEIGHTS[HEADER_UNGROUPED] = 2;
|
||||
HEADER_WEIGHTS[HEADER_PENDING_CONTACTS] = 3;
|
||||
|
||||
_converse.RosterGroupsComparator = function (a, b) {
|
||||
/* Groups are sorted alphabetically, ignoring case.
|
||||
* However, Ungrouped, Requesting Contacts and Pending Contacts
|
||||
* appear last and in that order.
|
||||
*/
|
||||
a = a.get('name');
|
||||
b = b.get('name');
|
||||
const special_groups = Object.keys(HEADER_WEIGHTS);
|
||||
const a_is_special = _.includes(special_groups, a);
|
||||
const b_is_special = _.includes(special_groups, b);
|
||||
if (!a_is_special && !b_is_special ) {
|
||||
return a.toLowerCase() < b.toLowerCase() ? -1 : (a.toLowerCase() > b.toLowerCase() ? 1 : 0);
|
||||
} else if (a_is_special && b_is_special) {
|
||||
return HEADER_WEIGHTS[a] < HEADER_WEIGHTS[b] ? -1 : (HEADER_WEIGHTS[a] > HEADER_WEIGHTS[b] ? 1 : 0);
|
||||
} else if (!a_is_special && b_is_special) {
|
||||
return (b === HEADER_REQUESTING_CONTACTS) ? 1 : -1;
|
||||
} else if (a_is_special && !b_is_special) {
|
||||
return (a === HEADER_REQUESTING_CONTACTS) ? -1 : 1;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
_converse.AddContactModal = _converse.BootstrapModal.extend({
|
||||
@ -678,7 +627,7 @@ converse.plugins.add('converse-rosterview', {
|
||||
let matches;
|
||||
q = q.toLowerCase();
|
||||
if (type === 'state') {
|
||||
if (this.model.get('name') === HEADER_REQUESTING_CONTACTS) {
|
||||
if (this.model.get('name') === _converse.HEADER_REQUESTING_CONTACTS) {
|
||||
// When filtering by chat state, we still want to
|
||||
// show requesting contacts, even though they don't
|
||||
// have the state in question.
|
||||
@ -747,13 +696,13 @@ converse.plugins.add('converse-rosterview', {
|
||||
},
|
||||
|
||||
onContactSubscriptionChange (contact) {
|
||||
if ((this.model.get('name') === HEADER_PENDING_CONTACTS) && contact.get('subscription') !== 'from') {
|
||||
if ((this.model.get('name') === _converse.HEADER_PENDING_CONTACTS) && contact.get('subscription') !== 'from') {
|
||||
this.removeContact(contact);
|
||||
}
|
||||
},
|
||||
|
||||
onContactRequestChange (contact) {
|
||||
if ((this.model.get('name') === HEADER_REQUESTING_CONTACTS) && !contact.get('requesting')) {
|
||||
if ((this.model.get('name') === _converse.HEADER_REQUESTING_CONTACTS) && !contact.get('requesting')) {
|
||||
this.removeContact(contact);
|
||||
}
|
||||
},
|
||||
@ -926,16 +875,16 @@ converse.plugins.add('converse-rosterview', {
|
||||
this.update();
|
||||
if (_.has(contact.changed, 'subscription')) {
|
||||
if (contact.changed.subscription === 'from') {
|
||||
this.addContactToGroup(contact, HEADER_PENDING_CONTACTS);
|
||||
this.addContactToGroup(contact, _converse.HEADER_PENDING_CONTACTS);
|
||||
} else if (_.includes(['both', 'to'], contact.get('subscription'))) {
|
||||
this.addExistingContact(contact);
|
||||
}
|
||||
}
|
||||
if (_.has(contact.changed, 'ask') && contact.changed.ask === 'subscribe') {
|
||||
this.addContactToGroup(contact, HEADER_PENDING_CONTACTS);
|
||||
this.addContactToGroup(contact, _converse.HEADER_PENDING_CONTACTS);
|
||||
}
|
||||
if (_.has(contact.changed, 'subscription') && contact.changed.requesting === 'true') {
|
||||
this.addContactToGroup(contact, HEADER_REQUESTING_CONTACTS);
|
||||
this.addContactToGroup(contact, _converse.HEADER_REQUESTING_CONTACTS);
|
||||
}
|
||||
this.updateFilter();
|
||||
},
|
||||
@ -961,10 +910,10 @@ converse.plugins.add('converse-rosterview', {
|
||||
if (_converse.roster_groups) {
|
||||
groups = contact.get('groups');
|
||||
if (groups.length === 0) {
|
||||
groups = [HEADER_UNGROUPED];
|
||||
groups = [_converse.HEADER_UNGROUPED];
|
||||
}
|
||||
} else {
|
||||
groups = [HEADER_CURRENT_CONTACTS];
|
||||
groups = [_converse.HEADER_CURRENT_CONTACTS];
|
||||
}
|
||||
_.each(groups, _.bind(this.addContactToGroup, this, contact, _, options));
|
||||
},
|
||||
@ -982,9 +931,9 @@ converse.plugins.add('converse-rosterview', {
|
||||
return;
|
||||
}
|
||||
if ((contact.get('ask') === 'subscribe') || (contact.get('subscription') === 'from')) {
|
||||
this.addContactToGroup(contact, HEADER_PENDING_CONTACTS, options);
|
||||
this.addContactToGroup(contact, _converse.HEADER_PENDING_CONTACTS, options);
|
||||
} else if (contact.get('requesting') === true) {
|
||||
this.addContactToGroup(contact, HEADER_REQUESTING_CONTACTS, options);
|
||||
this.addContactToGroup(contact, _converse.HEADER_REQUESTING_CONTACTS, options);
|
||||
}
|
||||
}
|
||||
return this;
|
||||
|
@ -82,7 +82,7 @@ converse.plugins.add('converse-singleton', {
|
||||
const { _converse } = this.__super__;
|
||||
if (_converse.isUniView()) {
|
||||
return false;
|
||||
} else {
|
||||
} else {
|
||||
return this.__super__.shouldShowOnTextMessage.apply(this, arguments);
|
||||
}
|
||||
},
|
||||
|
@ -40,13 +40,11 @@ converse.plugins.add('converse-bookmarks', {
|
||||
// New functions which don't exist yet can also be added.
|
||||
|
||||
ChatRoom: {
|
||||
|
||||
getAndPersistNickname(nick) {
|
||||
const { _converse } = this.__super__;
|
||||
nick = nick || _converse.getNicknameFromBookmark(this.get('jid'));
|
||||
return this.__super__.getAndPersistNickname.call(this, nick);
|
||||
},
|
||||
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -251,7 +251,7 @@ _converse.log = function (message, level, style='') {
|
||||
message = message.outerHTML;
|
||||
}
|
||||
const prefix = style ? '%c' : '';
|
||||
const logger = _.assign({
|
||||
const logger = Object.assign({
|
||||
'debug': _.get(console, 'log') ? console.log.bind(console) : _.noop,
|
||||
'error': _.get(console, 'log') ? console.log.bind(console) : _.noop,
|
||||
'info': _.get(console, 'log') ? console.log.bind(console) : _.noop,
|
||||
|
@ -29,9 +29,40 @@ converse.plugins.add('converse-mam', {
|
||||
// Overrides mentioned here will be picked up by converse.js's
|
||||
// plugin architecture they will replace existing methods on the
|
||||
// relevant objects or classes.
|
||||
//
|
||||
// New functions which don't exist yet can also be added.
|
||||
ChatBox: {
|
||||
async getDuplicateMessage (stanza) {
|
||||
const message = await this.__super__.getDuplicateMessage.apply(this, arguments);
|
||||
if (!message) {
|
||||
return this.findDuplicateFromArchiveID(stanza);
|
||||
}
|
||||
return message;
|
||||
},
|
||||
|
||||
getUpdatedMessageAttributes (message, stanza) {
|
||||
const attrs = this.__super__.getUpdatedMessageAttributes.apply(this, arguments);
|
||||
if (message && !message.get('is_archived')) {
|
||||
return Object.assign(attrs, {
|
||||
'is_archived': this.isArchived(stanza)
|
||||
}, this.getStanzaIDs(stanza))
|
||||
}
|
||||
return attrs;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
initialize () {
|
||||
/* The initialize function gets called as soon as the plugin is
|
||||
* loaded by Converse.js's plugin machinery.
|
||||
*/
|
||||
const { _converse } = this;
|
||||
|
||||
_converse.api.settings.update({
|
||||
archived_messages_page_size: '50',
|
||||
message_archiving: undefined, // Supported values are 'always', 'never', 'roster' (https://xmpp.org/extensions/xep-0313.html#prefs)
|
||||
message_archiving_timeout: 20000, // Time (in milliseconds) to wait before aborting MAM request
|
||||
});
|
||||
|
||||
const MAMEnabledChat = {
|
||||
|
||||
fetchNewestMessages () {
|
||||
/* Fetches messages that might have been archived *after*
|
||||
@ -40,7 +71,6 @@ converse.plugins.add('converse-mam', {
|
||||
if (this.disable_mam) {
|
||||
return;
|
||||
}
|
||||
const { _converse } = this.__super__;
|
||||
const most_recent_msg = u.getMostRecentMessage(this);
|
||||
|
||||
if (_.isNil(most_recent_msg)) {
|
||||
@ -59,7 +89,6 @@ converse.plugins.add('converse-mam', {
|
||||
if (this.disable_mam) {
|
||||
return;
|
||||
}
|
||||
const { _converse } = this.__super__;
|
||||
const is_groupchat = this.get('type') === CHATROOMS_TYPE;
|
||||
const mam_jid = is_groupchat ? this.get('jid') : _converse.bare_jid;
|
||||
if (!(await _converse.api.disco.supports(Strophe.NS.MAM, mam_jid))) {
|
||||
@ -92,7 +121,6 @@ converse.plugins.add('converse-mam', {
|
||||
},
|
||||
|
||||
async findDuplicateFromArchiveID (stanza) {
|
||||
const { _converse } = this.__super__;
|
||||
const result = sizzle(`result[xmlns="${Strophe.NS.MAM}"]`, stanza).pop();
|
||||
if (!result) {
|
||||
return null;
|
||||
@ -107,32 +135,11 @@ converse.plugins.add('converse-mam', {
|
||||
return this.messages.findWhere(query);
|
||||
},
|
||||
|
||||
async getDuplicateMessage (stanza) {
|
||||
const message = await this.__super__.getDuplicateMessage.apply(this, arguments);
|
||||
if (!message) {
|
||||
return this.findDuplicateFromArchiveID(stanza);
|
||||
}
|
||||
return message;
|
||||
},
|
||||
}
|
||||
Object.assign(_converse.ChatBox.prototype, MAMEnabledChat);
|
||||
|
||||
getUpdatedMessageAttributes (message, stanza) {
|
||||
const attrs = this.__super__.getUpdatedMessageAttributes.apply(this, arguments);
|
||||
if (message && !message.get('is_archived')) {
|
||||
return Object.assign(attrs, {
|
||||
'is_archived': this.isArchived(stanza)
|
||||
}, this.getStanzaIDs(stanza))
|
||||
}
|
||||
return attrs;
|
||||
}
|
||||
},
|
||||
|
||||
ChatRoom: {
|
||||
initialize () {
|
||||
this.__super__.initialize.apply(this, arguments);
|
||||
this.on('change:mam_enabled', this.fetchArchivedMessagesIfNecessary, this);
|
||||
this.on('change:connection_status', this.fetchArchivedMessagesIfNecessary, this);
|
||||
},
|
||||
|
||||
Object.assign(_converse.ChatRoom.prototype, {
|
||||
fetchArchivedMessagesIfNecessary () {
|
||||
if (this.get('connection_status') !== converse.ROOMSTATUS.ENTERED ||
|
||||
!this.get('mam_enabled') ||
|
||||
@ -142,22 +149,9 @@ converse.plugins.add('converse-mam', {
|
||||
this.fetchArchivedMessages();
|
||||
this.save({'mam_initialized': true});
|
||||
}
|
||||
},
|
||||
|
||||
},
|
||||
|
||||
initialize () {
|
||||
/* The initialize function gets called as soon as the plugin is
|
||||
* loaded by Converse.js's plugin machinery.
|
||||
*/
|
||||
const { _converse } = this;
|
||||
|
||||
_converse.api.settings.update({
|
||||
archived_messages_page_size: '50',
|
||||
message_archiving: undefined, // Supported values are 'always', 'never', 'roster' (https://xmpp.org/extensions/xep-0313.html#prefs)
|
||||
message_archiving_timeout: 20000, // Time (in milliseconds) to wait before aborting MAM request
|
||||
});
|
||||
|
||||
|
||||
_converse.onMAMError = function (iq) {
|
||||
if (iq.querySelectorAll('feature-not-implemented').length) {
|
||||
_converse.log(
|
||||
@ -220,6 +214,11 @@ converse.plugins.add('converse-mam', {
|
||||
_converse.api.listen.on('afterMessagesFetched', chat => chat.fetchNewestMessages());
|
||||
_converse.api.listen.on('chatReconnected', chat => chat.fetchNewestMessages());
|
||||
_converse.api.listen.on('addClientFeatures', () => _converse.api.disco.own.features.add(Strophe.NS.MAM));
|
||||
|
||||
_converse.api.listen.on('chatRoomOpened', (room) => {
|
||||
room.on('change:mam_enabled', room.fetchArchivedMessagesIfNecessary, room);
|
||||
room.on('change:connection_status', room.fetchArchivedMessagesIfNecessary, room);
|
||||
});
|
||||
/************************ END Event Handlers **************************/
|
||||
|
||||
|
||||
|
@ -36,6 +36,17 @@ converse.plugins.add('converse-roster', {
|
||||
'rosterInitialized',
|
||||
]);
|
||||
|
||||
_converse.HEADER_CURRENT_CONTACTS = __('My contacts');
|
||||
_converse.HEADER_PENDING_CONTACTS = __('Pending contacts');
|
||||
_converse.HEADER_REQUESTING_CONTACTS = __('Contact requests');
|
||||
_converse.HEADER_UNGROUPED = __('Ungrouped');
|
||||
|
||||
const HEADER_WEIGHTS = {};
|
||||
HEADER_WEIGHTS[_converse.HEADER_REQUESTING_CONTACTS] = 0;
|
||||
HEADER_WEIGHTS[_converse.HEADER_CURRENT_CONTACTS] = 1;
|
||||
HEADER_WEIGHTS[_converse.HEADER_UNGROUPED] = 2;
|
||||
HEADER_WEIGHTS[_converse.HEADER_PENDING_CONTACTS] = 3;
|
||||
|
||||
|
||||
_converse.registerPresenceHandler = function () {
|
||||
_converse.unregisterPresenceHandler();
|
||||
@ -381,6 +392,10 @@ converse.plugins.add('converse-roster', {
|
||||
model: _converse.RosterContact,
|
||||
|
||||
comparator (contact1, contact2) {
|
||||
/* Groups are sorted alphabetically, ignoring case.
|
||||
* However, Ungrouped, Requesting Contacts and Pending Contacts
|
||||
* appear last and in that order.
|
||||
*/
|
||||
const status1 = contact1.presence.get('show') || 'offline';
|
||||
const status2 = contact2.presence.get('show') || 'offline';
|
||||
if (_converse.STATUS_WEIGHTS[status1] === _converse.STATUS_WEIGHTS[status2]) {
|
||||
@ -854,6 +869,23 @@ converse.plugins.add('converse-roster', {
|
||||
_converse.RosterGroups = Backbone.Collection.extend({
|
||||
model: _converse.RosterGroup,
|
||||
|
||||
comparator (a, b) {
|
||||
a = a.get('name');
|
||||
b = b.get('name');
|
||||
const special_groups = Object.keys(HEADER_WEIGHTS);
|
||||
const a_is_special = _.includes(special_groups, a);
|
||||
const b_is_special = _.includes(special_groups, b);
|
||||
if (!a_is_special && !b_is_special ) {
|
||||
return a.toLowerCase() < b.toLowerCase() ? -1 : (a.toLowerCase() > b.toLowerCase() ? 1 : 0);
|
||||
} else if (a_is_special && b_is_special) {
|
||||
return HEADER_WEIGHTS[a] < HEADER_WEIGHTS[b] ? -1 : (HEADER_WEIGHTS[a] > HEADER_WEIGHTS[b] ? 1 : 0);
|
||||
} else if (!a_is_special && b_is_special) {
|
||||
return (b === _converse.HEADER_REQUESTING_CONTACTS) ? 1 : -1;
|
||||
} else if (a_is_special && !b_is_special) {
|
||||
return (a === _converse.HEADER_REQUESTING_CONTACTS) ? -1 : 1;
|
||||
}
|
||||
},
|
||||
|
||||
fetchRosterGroups () {
|
||||
/* Fetches all the roster groups from sessionStorage.
|
||||
*
|
||||
|
Loading…
Reference in New Issue
Block a user