Convert older docstrings to JSDoc syntax

This commit is contained in:
JC Brand 2019-03-29 23:47:56 +01:00
parent 7ed99092f5
commit a45bd8d14b
20 changed files with 1918 additions and 1734 deletions

View File

@ -261,4 +261,4 @@ html: dev docsdev apidoc
PHONY: apidoc PHONY: apidoc
apidoc: apidoc:
$(JSDOC) --readme docs/source/jsdoc_intro.md -c docs/source/conf.json -d docs/html/api src/*.js src/utils/*.js src/headless/*.js src/headless/utils/*.js $(JSDOC) --private --readme docs/source/jsdoc_intro.md -c docs/source/conf.json -d docs/html/api src/*.js src/utils/*.js src/headless/*.js src/headless/utils/*.js

1339
dist/converse.js vendored

File diff suppressed because it is too large Load Diff

View File

@ -41,6 +41,5 @@ to fix a bug or to add new functionality.
plugin_development plugin_development
api/index api/index
testing testing
events
other_frameworks other_frameworks
builds builds

View File

@ -3,29 +3,31 @@
Welcome to the new Converse API documentation, generated with Welcome to the new Converse API documentation, generated with
[JSDoc](http://usejsdoc.org/). [JSDoc](http://usejsdoc.org/).
The old (increasingly out of date and incomplete) API documentation is This documentation replaces the (increasingly out of date and incomplete) [old API documentation](/docs/html/developer_api.html)
currently still [available here](/docs/html/developer_api.html). and [old "Events and Promises" documentation](/docs/html/events.html).
## The public and private API ## The public and private API
Converse has a public API and a private API. Converse has a public API and a private API only available to plugins.
r
The reason we make this distinction between public and private is so that API The reason we make this distinction between public and private is so that API
methods which might can be used to "impersonate" the user, for example by methods which could be used to "impersonate" the user, for example by
sending messages on their behalf, are not available to random scripts running sending messages on their behalf, are not available to random scripts running
in the websites. in your website.
The public API is accessible via the `window.converse` global and is therefore The public API is accessible via the [window.converse](/docs/html/api/converse.html)
available to all JavaScript running in the page. global and is therefore available to any JavaScript running in the page.
Tehe private API is only accessible to plugins, which have been whitelisted and The private API is only accessible to plugins, which have been whitelisted and
registered before `converse.initialize` (which is a public API method) has been registered before [converse.initialize](/docs/html/api/converse.html#.initialize)
called. See the [plugin development](https://conversejs.org/docs/html/plugin_development.html) (which is a public API method) has been called.
See the [plugin development](/docs/html/plugin_development.html)
section for more info on writing plugins. section for more info on writing plugins.
Inside a plugin, you can get access to the `_converse.api` object. Note the Inside a plugin, you can get access to the {@link _converse.api}
underscore in front of `_converse`, which indicates that this is a private, object. Note the underscore in front of {@link _converse},
closured object. which indicates that this is a private, closured object.
## API Namespaces ## API Namespaces
@ -35,9 +37,26 @@ group relevant methods.
So, for example, all the XEP-0030 service discovery methods are under the So, for example, all the XEP-0030 service discovery methods are under the
{@link \_converse.api.disco} namespace, in the [private API]{@link \_converse.api}. {@link \_converse.api.disco} namespace, in the [private API]{@link \_converse.api}.
Which means that you access it via `_converse.api.disco`. Which means that you access it via {@link _converse.api.disco}.
Namespaces can be nested. So the {@link \_converse.api.disco} namespace ### Nested Namespaces
namespace has {@link \_converse.api.disco.own} as a nested namespace.
Namespaces can be nested.
{@link _converse.api} is the top-level namespace, of which {@link \_converse.api.disco}
is a nested, child namespace and {@link \_converse.api.disco.own} is nested another
level deeper.
Not all methods are however within a namespace. For example {@link converse.initialize}. Not all methods are however within a namespace. For example {@link converse.initialize}.
## Stable API versus unstable API
Converse uses [semantic versioning](https://semver.org/) for releases, which means that
we try to maintain a stable API for minor and patch releases and when we do change the
stable API we will make a major release.
In the JSDoc API documentation, all API methods that are **not** marked as *Private*
are considered to be part of the stable API, and you can therefore expect them to
not change between minor and patch releases. If a method is marked as *Private*,
then you could still use it, but we don't provide any guarantee that it won't change
between minor and patch releases.

View File

@ -586,18 +586,18 @@ converse.plugins.add('converse-chatview', {
); );
}, },
/**
* Inserts an indicator into the chat area, showing the
* day as given by the passed in date.
* The indicator is only inserted if necessary.
* @private
* @method _converse.ChatBoxView#insertDayIndicator
* @param { HTMLElement } next_msg_el - The message element before
* which the day indicator element must be inserted.
* This element must have a "data-isodate" attribute
* which specifies its creation date.
*/
insertDayIndicator (next_msg_el) { insertDayIndicator (next_msg_el) {
/* Inserts an indicator into the chat area, showing the
* day as given by the passed in date.
*
* The indicator is only inserted if necessary.
*
* Parameters:
* (HTMLElement) next_msg_el - The message element before
* which the day indicator element must be inserted.
* This element must have a "data-isodate" attribute
* which specifies its creation date.
*/
const prev_msg_el = u.getPreviousElement(next_msg_el, ".message:not(.chat-state-notification)"), const prev_msg_el = u.getPreviousElement(next_msg_el, ".message:not(.chat-state-notification)"),
prev_msg_date = _.isNull(prev_msg_el) ? null : prev_msg_el.getAttribute('data-isodate'), prev_msg_date = _.isNull(prev_msg_el) ? null : prev_msg_el.getAttribute('data-isodate'),
next_msg_date = next_msg_el.getAttribute('data-isodate'); next_msg_date = next_msg_el.getAttribute('data-isodate');
@ -616,13 +616,14 @@ converse.plugins.add('converse-chatview', {
} }
}, },
/**
* Return the ISO8601 format date of the latest message.
* @private
* @method _converse.ChatBoxView#getLastMessageDate
* @param { object } cutoff - Moment Date cutoff date. The last
* message received cutoff this date will be returned.
*/
getLastMessageDate (cutoff) { getLastMessageDate (cutoff) {
/* Return the ISO8601 format date of the latest message.
*
* Parameters:
* (Object) cutoff: Moment Date cutoff date. The last
* message received cutoff this date will be returned.
*/
const first_msg = u.getFirstChildElement(this.content, '.message:not(.chat-state-notification)'), const first_msg = u.getFirstChildElement(this.content, '.message:not(.chat-state-notification)'),
oldest_date = first_msg ? first_msg.getAttribute('data-isodate') : null; oldest_date = first_msg ? first_msg.getAttribute('data-isodate') : null;
if (!_.isNull(oldest_date) && moment(oldest_date).isAfter(cutoff)) { if (!_.isNull(oldest_date) && moment(oldest_date).isAfter(cutoff)) {
@ -713,13 +714,14 @@ converse.plugins.add('converse-chatview', {
return !u.isVisible(this.el); return !u.isVisible(this.el);
}, },
/**
* Given a view representing a message, insert it into the
* content area of the chat box.
* @private
* @method _converse.ChatBoxView#insertMessage
* @param { Backbone.View } message - The message Backbone.View
*/
insertMessage (view) { insertMessage (view) {
/* Given a view representing a message, insert it into the
* content area of the chat box.
*
* Parameters:
* (Backbone.View) message: The message Backbone.View
*/
if (view.model.get('type') === 'error') { if (view.model.get('type') === 'error') {
const previous_msg_el = this.content.querySelector(`[data-msgid="${view.model.get('msgid')}"]`); const previous_msg_el = this.content.querySelector(`[data-msgid="${view.model.get('msgid')}"]`);
if (previous_msg_el) { if (previous_msg_el) {
@ -746,20 +748,22 @@ converse.plugins.add('converse-chatview', {
return this.trigger('messageInserted', view.el); return this.trigger('messageInserted', view.el);
}, },
/**
* Given a message element, determine wether it should be
* marked as a followup message to the previous element.
*
* Also determine whether the element following it is a
* followup message or not.
*
* Followup messages are subsequent ones written by the same
* author with no other conversation elements inbetween and
* posted within 10 minutes of one another.
*
* @private
* @method _converse.ChatBoxView#markFollowups
* @param { HTMLElement } el - The message element
*/
markFollowups (el) { markFollowups (el) {
/* Given a message element, determine wether it should be
* marked as a followup message to the previous element.
*
* Also determine whether the element following it is a
* followup message or not.
*
* Followup messages are subsequent ones written by the same
* author with no other conversation elements inbetween and
* posted within 10 minutes of one another.
*
* Parameters:
* (HTMLElement) el - The message element.
*/
const from = el.getAttribute('data-from'), const from = el.getAttribute('data-from'),
previous_el = el.previousElementSibling, previous_el = el.previousElementSibling,
date = moment(el.getAttribute('data-isodate')), date = moment(el.getAttribute('data-isodate')),
@ -783,15 +787,14 @@ converse.plugins.add('converse-chatview', {
} }
}, },
/**
* Inserts a chat message into the content area of the chat box.
* Will also insert a new day indicator if the message is on a different day.
* @private
* @method _converse.ChatBoxView#showMessage
* @param { _converse.Message } message - The message object
*/
async showMessage (message) { async showMessage (message) {
/* Inserts a chat message into the content area of the chat box.
*
* Will also insert a new day indicator if the message is on a
* different day.
*
* Parameters:
* (Backbone.Model) message: The message object
*/
if (!u.isNewMessage(message) && u.isEmptyMessage(message)) { if (!u.isNewMessage(message) && u.isEmptyMessage(message)) {
// Handle archived or delayed messages without any message // Handle archived or delayed messages without any message
// text to show. // text to show.
@ -822,12 +825,13 @@ converse.plugins.add('converse-chatview', {
} }
}, },
/**
* Handler that gets called when a new message object is created.
* @private
* @method _converse.ChatBoxView#onMessageAdded
* @param { object } message - The message Backbone object that was added.
*/
onMessageAdded (message) { onMessageAdded (message) {
/* Handler that gets called when a new message object is created.
*
* Parameters:
* (Object) message - The message Backbone object that was added.
*/
this.showMessage(message); this.showMessage(message);
if (message.get('correcting')) { if (message.get('correcting')) {
this.insertIntoTextArea(message.get('message'), true, true); this.insertIntoTextArea(message.get('message'), true, true);
@ -865,17 +869,18 @@ converse.plugins.add('converse-chatview', {
} }
}, },
/**
* Mutator for setting the chat state of this chat session.
* Handles clearing of any chat state notification timeouts and
* setting new ones if necessary.
* Timeouts are set when the state being set is COMPOSING or PAUSED.
* After the timeout, COMPOSING will become PAUSED and PAUSED will become INACTIVE.
* See XEP-0085 Chat State Notifications.
* @private
* @method _converse.ChatBoxView#setChatState
* @param { string } state - The chat state (consts ACTIVE, COMPOSING, PAUSED, INACTIVE, GONE)
*/
setChatState (state, options) { setChatState (state, options) {
/* Mutator for setting the chat state of this chat session.
* Handles clearing of any chat state notification timeouts and
* setting new ones if necessary.
* Timeouts are set when the state being set is COMPOSING or PAUSED.
* After the timeout, COMPOSING will become PAUSED and PAUSED will become INACTIVE.
* See XEP-0085 Chat State Notifications.
*
* Parameters:
* (string) state - The chat state (consts ACTIVE, COMPOSING, PAUSED, INACTIVE, GONE)
*/
if (!_.isUndefined(this.chat_state_timeout)) { if (!_.isUndefined(this.chat_state_timeout)) {
window.clearTimeout(this.chat_state_timeout); window.clearTimeout(this.chat_state_timeout);
delete this.chat_state_timeout; delete this.chat_state_timeout;

View File

@ -378,11 +378,6 @@ converse.plugins.add('converse-controlbox', {
}, },
showHelpMessages () { showHelpMessages () {
/* Override showHelpMessages in ChatBoxView, for now do nothing.
*
* Parameters:
* (Array) msgs: Array of messages
*/
return; return;
} }
}); });

View File

@ -203,15 +203,12 @@ converse.plugins.add('converse-muc-views', {
}; };
/* Insert groupchat info (based on returned #disco IQ stanza)
* @function insertRoomInfo
* @param { HTMLElement } el - The HTML DOM element that contains the info.
* @param { XMLElement } stanza - The IQ stanza containing the groupchat info.
*/
function insertRoomInfo (el, stanza) { function insertRoomInfo (el, stanza) {
/* Insert groupchat info (based on returned #disco IQ stanza)
*
* Parameters:
* (HTMLElement) el: The HTML DOM element that should
* contain the info.
* (XMLElement) stanza: The IQ stanza containing the groupchat
* info.
*/
// All MUC features found here: https://xmpp.org/registrar/disco-features.html // All MUC features found here: https://xmpp.org/registrar/disco-features.html
el.querySelector('span.spinner').remove(); el.querySelector('span.spinner').remove();
el.querySelector('a.room-info').classList.add('selected'); el.querySelector('a.room-info').classList.add('selected');
@ -1110,12 +1107,13 @@ converse.plugins.add('converse-muc-views', {
this.model.addHandler('message', 'ChatRoomView.showStatusMessages', this.showStatusMessages.bind(this)); this.model.addHandler('message', 'ChatRoomView.showStatusMessages', this.showStatusMessages.bind(this));
}, },
/**
* Handles all MUC presence stanzas.
* @private
* @method _converse.ChatRoomView#onPresence
* @param { XMLElement } pres - The stanza
*/
onPresence (pres) { onPresence (pres) {
/* Handles all MUC presence stanzas.
*
* Parameters:
* (XMLElement) pres: The stanza
*/
// XXX: Current thinking is that excessive stanza // XXX: Current thinking is that excessive stanza
// processing inside a view is a "code smell". // processing inside a view is a "code smell".
// Instead stanza processing should happen inside the // Instead stanza processing should happen inside the
@ -1138,14 +1136,14 @@ converse.plugins.add('converse-muc-views', {
this.fetchMessages(); this.fetchMessages();
}, },
/**
* Join the groupchat.
* @private
* @method _converse.ChatRoomView#join
* @param { String } nick - The user's nickname
* @param { String } password - Optional password, if required by the groupchat
*/
join (nick, password) { join (nick, password) {
/* Join the groupchat.
*
* Parameters:
* (String) nick: The user's nickname
* (String) password: Optional password, if required by
* the groupchat.
*/
if (!nick && !this.model.get('nick')) { if (!nick && !this.model.get('nick')) {
this.checkForReservedNick(); this.checkForReservedNick();
return this; return this;
@ -1154,17 +1152,16 @@ converse.plugins.add('converse-muc-views', {
return this; return this;
}, },
/**
* Renders a form given an IQ stanza containing the current
* groupchat configuration.
* Returns a promise which resolves once the user has
* either submitted the form, or canceled it.
* @private
* @method _converse.ChatRoomView#renderConfigurationForm
* @param { XMLElement } stanza: The IQ stanza containing the groupchat config.
*/
renderConfigurationForm (stanza) { renderConfigurationForm (stanza) {
/* Renders a form given an IQ stanza containing the current
* groupchat configuration.
*
* Returns a promise which resolves once the user has
* either submitted the form, or canceled it.
*
* Parameters:
* (XMLElement) stanza: The IQ stanza containing the groupchat
* config.
*/
const container_el = this.el.querySelector('.chatroom-body'); const container_el = this.el.querySelector('.chatroom-body');
_.each(container_el.querySelectorAll('.chatroom-form-container'), u.removeElement); _.each(container_el.querySelectorAll('.chatroom-form-container'), u.removeElement);
_.each(container_el.children, u.hideElement); _.each(container_el.children, u.hideElement);
@ -1420,13 +1417,14 @@ converse.plugins.add('converse-muc-views', {
u.showElement(container); u.showElement(container);
}, },
/**
* @private
* @method _converse.ChatRoomView#getMessageFromStatus
* @param { XMLElement } stat: A <status> element
* @param { Boolean } is_self: Whether the element refers to the current user
* @param { XMLElement } stanza: The original stanza received
*/
getMessageFromStatus (stat, stanza, is_self) { getMessageFromStatus (stat, stanza, is_self) {
/* Parameters:
* (XMLElement) stat: A <status> element.
* (Boolean) is_self: Whether the element refers to the
* current user.
* (XMLElement) stanza: The original stanza received.
*/
const code = stat.getAttribute('code'); const code = stat.getAttribute('code');
if (code === '110' || (code === '100' && !is_self)) { return; } if (code === '110' || (code === '100' && !is_self)) { return; }
if (code in _converse.muc.info_messages) { if (code in _converse.muc.info_messages) {
@ -1697,14 +1695,14 @@ converse.plugins.add('converse-muc-views', {
this.scrollDown(); this.scrollDown();
}, },
/**
* Check for status codes and communicate their purpose to the user.
* See: https://xmpp.org/registrar/mucstatus.html
* @private
* @method _converse.ChatRoomView#showStatusMessages
* @param { XMLElement } stanza - The message or presence stanza containing the status codes
*/
showStatusMessages (stanza) { showStatusMessages (stanza) {
/* Check for status codes and communicate their purpose to the user.
* See: https://xmpp.org/registrar/mucstatus.html
*
* Parameters:
* (XMLElement) stanza: The message or presence stanza
* containing the status codes.
*/
const elements = sizzle(`x[xmlns="${Strophe.NS.MUC_USER}"]`, stanza); const elements = sizzle(`x[xmlns="${Strophe.NS.MUC_USER}"]`, stanza);
const is_self = stanza.querySelectorAll("status[code='110']").length; const is_self = stanza.querySelectorAll("status[code='110']").length;
const iteratee = _.partial(this.parseXUserElement.bind(this), _, stanza, is_self); const iteratee = _.partial(this.parseXUserElement.bind(this), _, stanza, is_self);

View File

@ -159,6 +159,11 @@ converse.plugins.add('converse-register', {
} }
}); });
/**
* @class
* @namespace _converse.RegisterPanel
* @memberOf _converse
*/
_converse.RegisterPanel = Backbone.NativeView.extend({ _converse.RegisterPanel = Backbone.NativeView.extend({
tagName: 'div', tagName: 'div',
id: "converse-register-panel", id: "converse-register-panel",
@ -200,20 +205,21 @@ converse.plugins.add('converse-register', {
if (!this._registering) { if (!this._registering) {
connect_cb(req, callback, raw); connect_cb(req, callback, raw);
} else { } else {
if (this.getRegistrationFields(req, callback, raw)) { if (this.getRegistrationFields(req, callback)) {
this._registering = false; this._registering = false;
} }
} }
}; };
}, },
getRegistrationFields (req, _callback, raw) { /**
/* Send an IQ stanza to the XMPP server asking for the * Send an IQ stanza to the XMPP server asking for the registration fields.
* registration fields. * @private
* Parameters: * @method _converse.RegisterPanel#getRegistrationFields
* (Strophe.Request) req - The current request * @param { Strophe.Request } req - The current request
* (Function) callback * @param { Function } callback - The callback function
*/ */
getRegistrationFields (req, _callback) {
const conn = _converse.connection; const conn = _converse.connection;
conn.connected = true; conn.connected = true;
@ -248,12 +254,13 @@ converse.plugins.add('converse-register', {
return true; return true;
}, },
/**
* Handler for {@link _converse.RegisterPanel#getRegistrationFields}
* @private
* @method _converse.RegisterPanel#onRegistrationFields
* @param { XMLElement } stanza - The query stanza.
*/
onRegistrationFields (stanza) { onRegistrationFields (stanza) {
/* Handler for Registration Fields Request.
*
* Parameters:
* (XMLElement) elem - The query stanza.
*/
if (stanza.getAttribute("type") === "error") { if (stanza.getAttribute("type") === "error") {
_converse.connection._changeConnectStatus( _converse.connection._changeConnectStatus(
Strophe.Status.REGIFAIL, Strophe.Status.REGIFAIL,
@ -309,13 +316,13 @@ converse.plugins.add('converse-register', {
}, },
/**
* Callback method that gets called when the user has chosen an XMPP provider
* @private
* @method _converse.RegisterPanel#onProviderChosen
* @param { HTMLElement } form - The form that was submitted
*/
onProviderChosen (form) { onProviderChosen (form) {
/* Callback method that gets called when the user has chosen an
* XMPP provider.
*
* Parameters:
* (HTMLElement) form - The form that was submitted
*/
const domain_input = form.querySelector('input[name=domain]'), const domain_input = form.querySelector('input[name=domain]'),
domain = _.get(domain_input, 'value'); domain = _.get(domain_input, 'value');
if (!domain) { if (!domain) {
@ -327,13 +334,13 @@ converse.plugins.add('converse-register', {
this.fetchRegistrationForm(domain.trim()); this.fetchRegistrationForm(domain.trim());
}, },
/**
* Fetch a registration form from the requested domain
* @private
* @method _converse.RegisterPanel#fetchRegistrationForm
* @param { String } domain_name - XMPP server domain
*/
fetchRegistrationForm (domain_name) { fetchRegistrationForm (domain_name) {
/* This is called with a domain name based on which, it fetches a
* registration form from the requested domain.
*
* Parameters:
* (String) domain_name - XMPP server domain
*/
if (!this.model.get('registration_form_rendered')) { if (!this.model.get('registration_form_rendered')) {
this.renderRegistrationRequest(); this.renderRegistrationRequest();
} }
@ -386,16 +393,14 @@ converse.plugins.add('converse-register', {
return this; return this;
}, },
/**
* Callback function called by Strophe whenever the connection status changes.
* Passed to Strophe specifically during a registration attempt.
* @private
* @method _converse.RegisterPanel#onConnectStatusChanged
* @param { integer } status_code - The Strophe.Status status code
*/
onConnectStatusChanged(status_code) { onConnectStatusChanged(status_code) {
/* Callback function called by Strophe whenever the
* connection status changes.
*
* Passed to Strophe specifically during a registration
* attempt.
*
* Parameters:
* (Integer) status_code - The Stroph.Status status code
*/
_converse.log('converse-register: onConnectStatusChanged'); _converse.log('converse-register: onConnectStatusChanged');
if (_.includes([ if (_.includes([
Strophe.Status.DISCONNECTED, Strophe.Status.DISCONNECTED,
@ -472,13 +477,14 @@ converse.plugins.add('converse-register', {
}); });
}, },
/**
* Renders the registration form based on the XForm fields
* received from the XMPP server.
* @private
* @method _converse.RegisterPanel#renderRegistrationForm
* @param { XMLElement } stanza - The IQ stanza received from the XMPP server.
*/
renderRegistrationForm (stanza) { renderRegistrationForm (stanza) {
/* Renders the registration form based on the XForm fields
* received from the XMPP server.
*
* Parameters:
* (XMLElement) stanza - The IQ stanza received from the XMPP server.
*/
const form = this.el.querySelector('form'); const form = this.el.querySelector('form');
form.innerHTML = tpl_registration_form({ form.innerHTML = tpl_registration_form({
'__': _converse.__, '__': _converse.__,
@ -528,14 +534,14 @@ converse.plugins.add('converse-register', {
flash.classList.remove('hidden'); flash.classList.remove('hidden');
}, },
/**
* Report back to the user any error messages received from the
* XMPP server after attempted registration.
* @private
* @method _converse.RegisterPanel#reportErrors
* @param { XMLElement } stanza - The IQ stanza received from the XMPP server
*/
reportErrors (stanza) { reportErrors (stanza) {
/* Report back to the user any error messages received from the
* XMPP server after attempted registration.
*
* Parameters:
* (XMLElement) stanza - The IQ stanza received from the
* XMPP server.
*/
const errors = stanza.querySelectorAll('error'); const errors = stanza.querySelectorAll('error');
_.each(errors, (error) => { _.each(errors, (error) => {
this.showValidationError(error.textContent); this.showValidationError(error.textContent);
@ -568,14 +574,14 @@ converse.plugins.add('converse-register', {
} }
}, },
/**
* Handler, when the user submits the registration form.
* Provides form error feedback or starts the registration process.
* @private
* @method _converse.RegisterPanel#submitRegistrationForm
* @param { HTMLElement } form - The HTML form that was submitted
*/
submitRegistrationForm (form) { submitRegistrationForm (form) {
/* Handler, when the user submits the registration form.
* Provides form error feedback or starts the registration
* process.
*
* Parameters:
* (HTMLElement) form - The HTML form that was submitted
*/
const has_empty_inputs = _.reduce( const has_empty_inputs = _.reduce(
this.el.querySelectorAll('input.required'), this.el.querySelectorAll('input.required'),
function (result, input) { function (result, input) {
@ -606,13 +612,12 @@ converse.plugins.add('converse-register', {
this.setFields(iq.tree()); this.setFields(iq.tree());
}, },
/* Stores the values that will be sent to the XMPP server during attempted registration.
* @private
* @method _converse.RegisterPanel#setFields
* @param { XMLElement } stanza - the IQ stanza that will be sent to the XMPP server.
*/
setFields (stanza) { setFields (stanza) {
/* Stores the values that will be sent to the XMPP server
* during attempted registration.
*
* Parameters:
* (XMLElement) stanza - the IQ stanza that will be sent to the XMPP server.
*/
const query = stanza.querySelector('query'); const query = stanza.querySelector('query');
const xform = sizzle(`x[xmlns="${Strophe.NS.XFORM}"]`, query); const xform = sizzle(`x[xmlns="${Strophe.NS.XFORM}"]`, query);
if (xform.length > 0) { if (xform.length > 0) {
@ -653,14 +658,15 @@ converse.plugins.add('converse-register', {
this.form_type = 'xform'; this.form_type = 'xform';
}, },
/**
* Callback method that gets called when a return IQ stanza
* is received from the XMPP server, after attempting to
* register a new user.
* @private
* @method _converse.RegisterPanel#reportErrors
* @param { XMLElement } stanza - The IQ stanza.
*/
_onRegisterIQ (stanza) { _onRegisterIQ (stanza) {
/* Callback method that gets called when a return IQ stanza
* is received from the XMPP server, after attempting to
* register a new user.
*
* Parameters:
* (XMLElement) stanza - The IQ stanza.
*/
if (stanza.getAttribute("type") === "error") { if (stanza.getAttribute("type") === "error") {
_converse.log("Registration failed.", Strophe.LogLevel.ERROR); _converse.log("Registration failed.", Strophe.LogLevel.ERROR);
this.reportErrors(stanza); this.reportErrors(stanza);

View File

@ -442,13 +442,13 @@ converse.plugins.add('converse-chatboxes', {
return false; return false;
}, },
/**
* Given a {@link _converse.Message} return the XML stanza that represents it.
* @private
* @method _converse.ChatBox#createMessageStanza
* @param { _converse.Message } message - The message object
*/
createMessageStanza (message) { createMessageStanza (message) {
/* Given a _converse.Message Backbone.Model, return the XML
* stanza that represents it.
*
* Parameters:
* (Object) message - The Backbone.Model representing the message
*/
const stanza = $msg({ const stanza = $msg({
'from': _converse.connection.jid, 'from': _converse.connection.jid,
'to': this.get('jid'), 'to': this.get('jid'),
@ -630,13 +630,14 @@ converse.plugins.add('converse-chatboxes', {
}); });
}, },
/**
* Extract the XEP-0359 stanza IDs from the passed in stanza
* and return a map containing them.
* @private
* @method _converse.ChatBox#getStanzaIDs
* @param { XMLElement } stanza - The message stanza
*/
getStanzaIDs (stanza) { getStanzaIDs (stanza) {
/* Extract the XEP-0359 stanza IDs from the passed in stanza
* and return a map containing them.
*
* Parameters:
* (XMLElement) stanza - The message stanza
*/
const attrs = {}; const attrs = {};
const stanza_ids = sizzle(`stanza-id[xmlns="${Strophe.NS.SID}"]`, stanza); const stanza_ids = sizzle(`stanza-id[xmlns="${Strophe.NS.SID}"]`, stanza);
if (stanza_ids.length) { if (stanza_ids.length) {
@ -659,18 +660,17 @@ converse.plugins.add('converse-chatboxes', {
return !_.isNil(sizzle(`result[xmlns="${Strophe.NS.MAM}"]`, original_stanza).pop()); return !_.isNil(sizzle(`result[xmlns="${Strophe.NS.MAM}"]`, original_stanza).pop());
}, },
/**
* Parses a passed in message stanza and returns an object
* of attributes.
* @private
* @method _converse.ChatBox#getMessageAttributesFromStanza
* @param { XMLElement } stanza - The message stanza
* @param { XMLElement } delay - The <delay> node from the stanza, if there was one.
* @param { XMLElement } original_stanza - The original stanza, that contains the
* message stanza, if it was contained, otherwise it's the message stanza itself.
*/
getMessageAttributesFromStanza (stanza, original_stanza) { getMessageAttributesFromStanza (stanza, original_stanza) {
/* Parses a passed in message stanza and returns an object
* of attributes.
*
* Parameters:
* (XMLElement) stanza - The message stanza
* (XMLElement) delay - The <delay> node from the
* stanza, if there was one.
* (XMLElement) original_stanza - The original stanza,
* that contains the message stanza, if it was
* contained, otherwise it's the message stanza itself.
*/
const spoiler = sizzle(`spoiler[xmlns="${Strophe.NS.SPOILER}"]`, original_stanza).pop(), const spoiler = sizzle(`spoiler[xmlns="${Strophe.NS.SPOILER}"]`, original_stanza).pop(),
delay = sizzle(`delay[xmlns="${Strophe.NS.DELAY}"]`, original_stanza).pop(), delay = sizzle(`delay[xmlns="${Strophe.NS.DELAY}"]`, original_stanza).pop(),
text = _converse.chatboxes.getMessageBody(stanza) || undefined, text = _converse.chatboxes.getMessageBody(stanza) || undefined,
@ -864,13 +864,13 @@ converse.plugins.add('converse-chatboxes', {
} }
}, },
/**
* Handler method for all incoming single-user chat "message" stanzas.
* @private
* @method _converse.ChatBox#onMessage
* @param { XMLElement } stanza - The incoming message stanza
*/
async onMessage (stanza) { async onMessage (stanza) {
/* Handler method for all incoming single-user chat "message"
* stanzas.
*
* Parameters:
* (XMLElement) stanza - The incoming message stanza
*/
let to_jid = stanza.getAttribute('to'); let to_jid = stanza.getAttribute('to');
const to_resource = Strophe.getResourceFromJid(to_jid); const to_resource = Strophe.getResourceFromJid(to_jid);
@ -970,15 +970,16 @@ converse.plugins.add('converse-chatboxes', {
_converse.api.trigger('message', {'stanza': original_stanza, 'chatbox': chatbox}); _converse.api.trigger('message', {'stanza': original_stanza, 'chatbox': chatbox});
}, },
/**
* Returns a chat box or optionally return a newly
* created one if one doesn't exist.
* @private
* @method _converse.ChatBox#getChatBox
* @param { string } jid - The JID of the user whose chat box we want
* @param { boolean } create - Should a new chat box be created if none exists?
* @param { object } attrs - Optional chat box atributes.
*/
getChatBox (jid, attrs={}, create) { getChatBox (jid, attrs={}, create) {
/* Returns a chat box or optionally return a newly
* created one if one doesn't exist.
*
* Parameters:
* (String) jid - The JID of the user whose chat box we want
* (Boolean) create - Should a new chat box be created if none exists?
* (Object) attrs - Optional chat box atributes.
*/
if (_.isObject(jid)) { if (_.isObject(jid)) {
create = attrs; create = attrs;
attrs = jid; attrs = jid;

View File

@ -225,20 +225,19 @@ _converse.default_settings = {
}; };
/**
* Logs messages to the browser's developer console.
* Available loglevels are 0 for 'debug', 1 for 'info', 2 for 'warn',
* 3 for 'error' and 4 for 'fatal'.
* When using the 'error' or 'warn' loglevels, a full stacktrace will be
* logged as well.
* @method log
* @private
* @memberOf _converse
* @param { string } message - The message to be logged
* @param { integer } level - The loglevel which allows for filtering of log messages
*/
_converse.log = function (message, level, style='') { _converse.log = function (message, level, style='') {
/* Logs messages to the browser's developer console.
*
* Parameters:
* (String) message - The message to be logged.
* (Integer) level - The loglevel which allows for filtering of log
* messages.
*
* Available loglevels are 0 for 'debug', 1 for 'info', 2 for 'warn',
* 3 for 'error' and 4 for 'fatal'.
*
* When using the 'error' or 'warn' loglevels, a full stacktrace will be
* logged as well.
*/
if (level === Strophe.LogLevel.ERROR || level === Strophe.LogLevel.FATAL) { if (level === Strophe.LogLevel.ERROR || level === Strophe.LogLevel.FATAL) {
style = style || 'color: maroon'; style = style || 'color: maroon';
} }
@ -275,12 +274,15 @@ Strophe.log = function (level, msg) { _converse.log(level+' '+msg, level); };
Strophe.error = function (msg) { _converse.log(msg, Strophe.LogLevel.ERROR); }; Strophe.error = function (msg) { _converse.log(msg, Strophe.LogLevel.ERROR); };
/**
* Translate the given string based on the current locale.
* Handles all MUC presence stanzas.
* @method __
* @private
* @memberOf _converse
* @param { String } str - The string to translate
*/
_converse.__ = function (str) { _converse.__ = function (str) {
/* Translate the given string based on the current locale.
*
* Parameters:
* (String) str - The string to translate.
*/
if (_.isUndefined(i18n)) { if (_.isUndefined(i18n)) {
return str; return str;
} }
@ -565,12 +567,14 @@ _converse.initialize = async function (settings, callback) {
this.generateResource = () => `/converse.js-${Math.floor(Math.random()*139749528).toString()}`; this.generateResource = () => `/converse.js-${Math.floor(Math.random()*139749528).toString()}`;
/**
* Send out a Chat Status Notification (XEP-0352)
* @private
* @method sendCSI
* @memberOf _converse
* @param { String } stat - The user's chat status
*/
this.sendCSI = function (stat) { this.sendCSI = function (stat) {
/* Send out a Chat Status Notification (XEP-0352)
*
* Parameters:
* (String) stat: The user's chat status
*/
_converse.api.send($build(stat, {xmlns: Strophe.NS.CSI})); _converse.api.send($build(stat, {xmlns: Strophe.NS.CSI}));
_converse.inactive = (stat === _converse.INACTIVE) ? true : false; _converse.inactive = (stat === _converse.INACTIVE) ? true : false;
}; };
@ -661,14 +665,15 @@ _converse.initialize = async function (settings, callback) {
}); });
}; };
/**
* Reject or cancel another user's subscription to our presence updates.
* @method rejectPresenceSubscription
* @private
* @memberOf _converse
* @param { String } jid - The Jabber ID of the user whose subscription is being canceled
* @param { String } message - An optional message to the user
*/
this.rejectPresenceSubscription = function (jid, message) { this.rejectPresenceSubscription = function (jid, message) {
/* Reject or cancel another user's subscription to our presence updates.
*
* Parameters:
* (String) jid - The Jabber ID of the user whose subscription
* is being canceled.
* (String) message - An optional message to the user
*/
const pres = $pres({to: jid, type: "unsubscribed"}); const pres = $pres({to: jid, type: "unsubscribed"});
if (message && message !== "") { pres.c("status").t(message); } if (message && message !== "") { pres.c("status").t(message); }
_converse.api.send(pres); _converse.api.send(pres);

View File

@ -23,6 +23,11 @@ converse.plugins.add('converse-disco', {
_converse.api.promises.add('discoInitialized'); _converse.api.promises.add('discoInitialized');
/**
* @class
* @namespace _converse.DiscoEntity
* @memberOf _converse
*/
_converse.DiscoEntity = Backbone.Model.extend({ _converse.DiscoEntity = Backbone.Model.extend({
/* A Disco Entity is a JID addressable entity that can be queried /* A Disco Entity is a JID addressable entity that can be queried
* for features. * for features.
@ -64,14 +69,15 @@ converse.plugins.add('converse-disco', {
this.items.fetch(); this.items.fetch();
}, },
/**
* Returns a Promise which resolves with a map indicating
* whether a given identity is provided by this entity.
* @private
* @method _converse.DiscoEntity#getIdentity
* @param { String } category - The identity category
* @param { String } type - The identity type
*/
async getIdentity (category, type) { async getIdentity (category, type) {
/* Returns a Promise which resolves with a map indicating
* whether a given identity is provided by this entity.
*
* Parameters:
* (String) category - The identity category
* (String) type - The identity type
*/
await this.waitUntilFeaturesDiscovered; await this.waitUntilFeaturesDiscovered;
return this.identities.findWhere({ return this.identities.findWhere({
'category': category, 'category': category,
@ -79,13 +85,14 @@ converse.plugins.add('converse-disco', {
}); });
}, },
/**
* Returns a Promise which resolves with a map indicating
* whether a given feature is supported.
* @private
* @method _converse.DiscoEntity#hasFeature
* @param { String } feature - The feature that might be supported.
*/
async hasFeature (feature) { async hasFeature (feature) {
/* Returns a Promise which resolves with a map indicating
* whether a given feature is supported.
*
* Parameters:
* (String) feature - The feature that might be supported.
*/
await this.waitUntilFeaturesDiscovered await this.waitUntilFeaturesDiscovered
if (this.features.findWhere({'var': feature})) { if (this.features.findWhere({'var': feature})) {
return this; return this;

View File

@ -291,14 +291,14 @@ converse.plugins.add('converse-muc', {
return this.get('name') || this.get('jid'); return this.get('name') || this.get('jid');
}, },
/**
* Join the groupchat.
* @private
* @method _converse.ChatRoom#join
* @param { String } nick - The user's nickname
* @param { String } password - Optional password, if required by the groupchat.
*/
join (nick, password) { join (nick, password) {
/* Join the groupchat.
*
* Parameters:
* (String) nick: The user's nickname
* (String) password: Optional password, if required by
* the groupchat.
*/
nick = nick ? nick : this.get('nick'); nick = nick ? nick : this.get('nick');
if (!nick) { if (!nick) {
throw new TypeError('join: You need to provide a valid nickname'); throw new TypeError('join: You need to provide a valid nickname');
@ -322,13 +322,12 @@ converse.plugins.add('converse-muc', {
return this; return this;
}, },
/* Leave the groupchat.
* @private
* @method _converse.ChatRoom#leave
* @param { string } exit_msg - Optional message to indicate your reason for leaving
*/
leave (exit_msg) { leave (exit_msg) {
/* Leave the groupchat.
*
* Parameters:
* (String) exit_msg: Optional message to indicate your
* reason for leaving.
*/
this.features.destroy(); this.features.destroy();
this.occupants.browserStorage._clear(); this.occupants.browserStorage._clear();
this.occupants.reset(); this.occupants.reset();
@ -479,12 +478,14 @@ converse.plugins.add('converse-muc', {
); );
}, },
/**
* Send a direct invitation as per XEP-0249
* @private
* @method _converse.ChatRoom#directInvite
* @param { String } recipient - JID of the person being invited
* @param { String } reason - Optional reason for the invitation
*/
directInvite (recipient, reason) { directInvite (recipient, reason) {
/* Send a direct invitation as per XEP-0249
* Parameters:
* (String) recipient - JID of the person being invited
* (String) reason - Optional reason for the invitation
*/
if (this.features.get('membersonly')) { if (this.features.get('membersonly')) {
// When inviting to a members-only groupchat, we first add // When inviting to a members-only groupchat, we first add
// the person to the member list by giving them an // the person to the member list by giving them an
@ -565,20 +566,17 @@ converse.plugins.add('converse-muc', {
this.features.save(attrs); this.features.save(attrs);
}, },
/* Send an IQ stanza to the server, asking it for the
* member-list of this groupchat.
* See: https://xmpp.org/extensions/xep-0045.html#modifymember
* @private
* @method _converse.ChatRoom#requestMemberList
* @param { string } affiliation - The specific member list to
* fetch. 'admin', 'owner' or 'member'.
* @returns:
* A promise which resolves once the list has been retrieved.
*/
requestMemberList (affiliation) { requestMemberList (affiliation) {
/* Send an IQ stanza to the server, asking it for the
* member-list of this groupchat.
*
* See: https://xmpp.org/extensions/xep-0045.html#modifymember
*
* Parameters:
* (String) affiliation: The specific member list to
* fetch. 'admin', 'owner' or 'member'.
*
* Returns:
* A promise which resolves once the list has been
* retrieved.
*/
affiliation = affiliation || 'member'; affiliation = affiliation || 'member';
const iq = $iq({to: this.get('jid'), type: "get"}) const iq = $iq({to: this.get('jid'), type: "get"})
.c("query", {xmlns: Strophe.NS.MUC_ADMIN}) .c("query", {xmlns: Strophe.NS.MUC_ADMIN})
@ -586,28 +584,26 @@ converse.plugins.add('converse-muc', {
return _converse.api.sendIQ(iq); return _converse.api.sendIQ(iq);
}, },
/**
* Send IQ stanzas to the server to set an affiliation for
* the provided JIDs.
* See: https://xmpp.org/extensions/xep-0045.html#modifymember
*
* Prosody doesn't accept multiple JIDs' affiliations
* being set in one IQ stanza, so as a workaround we send
* a separate stanza for each JID.
* Related ticket: https://issues.prosody.im/345
*
* @private
* @method _converse.ChatRoom#setAffiliation
* @param { string } affiliation - The affiliation
* @param { object } members - A map of jids, affiliations and
* optionally reasons. Only those entries with the
* same affiliation as being currently set will be considered.
* @returns
* A promise which resolves and fails depending on the XMPP server response.
*/
setAffiliation (affiliation, members) { setAffiliation (affiliation, members) {
/* Send IQ stanzas to the server to set an affiliation for
* the provided JIDs.
*
* See: https://xmpp.org/extensions/xep-0045.html#modifymember
*
* XXX: Prosody doesn't accept multiple JIDs' affiliations
* being set in one IQ stanza, so as a workaround we send
* a separate stanza for each JID.
* Related ticket: https://issues.prosody.im/345
*
* Parameters:
* (String) affiliation: The affiliation
* (Object) members: A map of jids, affiliations and
* optionally reasons. Only those entries with the
* same affiliation as being currently set will be
* considered.
*
* Returns:
* A promise which resolves and fails depending on the
* XMPP server response.
*/
members = _.filter(members, (member) => members = _.filter(members, (member) =>
// We only want those members who have the right // We only want those members who have the right
// affiliation (or none, which implies the provided one). // affiliation (or none, which implies the provided one).
@ -618,18 +614,19 @@ converse.plugins.add('converse-muc', {
return Promise.all(promises); return Promise.all(promises);
}, },
/**
* Submit the groupchat configuration form by sending an IQ
* stanza to the server.
* @private
* @method _converse.ChatRoom#saveConfiguration
* @param { HTMLElement } form - The configuration form DOM element.
* If no form is provided, the default configuration
* values will be used.
* @returns { promise }
* Returns a promise which resolves once the XMPP server
* has return a response IQ.
*/
saveConfiguration (form) { saveConfiguration (form) {
/* Submit the groupchat configuration form by sending an IQ
* stanza to the server.
*
* Returns a promise which resolves once the XMPP server
* has return a response IQ.
*
* Parameters:
* (HTMLElement) form: The configuration form DOM element.
* If no form is provided, the default configuration
* values will be used.
*/
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const inputs = form ? sizzle(':input:not([type=button]):not([type=submit])', form) : [], const inputs = form ? sizzle(':input:not([type=button]):not([type=submit])', form) : [],
configArray = _.map(inputs, u.webForm2xForm); configArray = _.map(inputs, u.webForm2xForm);
@ -689,20 +686,21 @@ converse.plugins.add('converse-muc', {
); );
}, },
/**
* Send an IQ stanza with the groupchat configuration.
* @private
* @method _converse.ChatRoom#sendConfiguration
* @param { Array } config - The groupchat configuration
* @param { Function } callback - Callback upon succesful IQ response
* The first parameter passed in is IQ containing the
* groupchat configuration.
* The second is the response IQ from the server.
* @param { Function } errback - Callback upon error IQ response
* The first parameter passed in is IQ containing the
* groupchat configuration.
* The second is the response IQ from the server.
*/
sendConfiguration (config, callback, errback) { sendConfiguration (config, callback, errback) {
/* Send an IQ stanza with the groupchat configuration.
*
* Parameters:
* (Array) config: The groupchat configuration
* (Function) callback: Callback upon succesful IQ response
* The first parameter passed in is IQ containing the
* groupchat configuration.
* The second is the response IQ from the server.
* (Function) errback: Callback upon error IQ response
* The first parameter passed in is IQ containing the
* groupchat configuration.
* The second is the response IQ from the server.
*/
const iq = $iq({to: this.get('jid'), type: "set"}) const iq = $iq({to: this.get('jid'), type: "set"})
.c("query", {xmlns: Strophe.NS.MUC_OWNER}) .c("query", {xmlns: Strophe.NS.MUC_OWNER})
.c("x", {xmlns: Strophe.NS.XFORM, type: "submit"}); .c("x", {xmlns: Strophe.NS.XFORM, type: "submit"});
@ -712,13 +710,13 @@ converse.plugins.add('converse-muc', {
return _converse.api.sendIQ(iq).then(callback).catch(errback); return _converse.api.sendIQ(iq).then(callback).catch(errback);
}, },
/**
* Parse the presence stanza for the current user's affiliation.
* @private
* @method _converse.ChatRoom#saveAffiliationAndRole
* @param { XMLElement } pres - A <presence> stanza.
*/
saveAffiliationAndRole (pres) { saveAffiliationAndRole (pres) {
/* Parse the presence stanza for the current user's
* affiliation.
*
* Parameters:
* (XMLElement) pres: A <presence> stanza.
*/
const item = sizzle(`x[xmlns="${Strophe.NS.MUC_USER}"] item`, pres).pop(); const item = sizzle(`x[xmlns="${Strophe.NS.MUC_USER}"] item`, pres).pop();
const is_self = pres.querySelector("status[code='110']"); const is_self = pres.querySelector("status[code='110']");
if (is_self && !_.isNil(item)) { if (is_self && !_.isNil(item)) {
@ -733,15 +731,16 @@ converse.plugins.add('converse-muc', {
} }
}, },
/**
* Send an IQ stanza specifying an affiliation change.
* @private
* @method _converse.ChatRoom#
* @param { String } affiliation: affiliation
* (could also be stored on the member object).
* @param { Object } member: Map containing the member's jid and
* optionally a reason and affiliation.
*/
sendAffiliationIQ (affiliation, member) { sendAffiliationIQ (affiliation, member) {
/* Send an IQ stanza specifying an affiliation change.
*
* Paremeters:
* (String) affiliation: affiliation (could also be stored
* on the member object).
* (Object) member: Map containing the member's jid and
* optionally a reason and affiliation.
*/
const iq = $iq({to: this.get('jid'), type: "set"}) const iq = $iq({to: this.get('jid'), type: "set"})
.c("query", {xmlns: Strophe.NS.MUC_ADMIN}) .c("query", {xmlns: Strophe.NS.MUC_ADMIN})
.c("item", { .c("item", {
@ -755,17 +754,17 @@ converse.plugins.add('converse-muc', {
return _converse.api.sendIQ(iq); return _converse.api.sendIQ(iq);
}, },
/**
* Send IQ stanzas to the server to modify the
* affiliations in this groupchat.
* See: https://xmpp.org/extensions/xep-0045.html#modifymember
* @private
* @method _converse.ChatRoom#setAffiliations
* @param { object } members - A map of jids, affiliations and optionally reasons
* @param { function } onSuccess - callback for a succesful response
* @param { function } onError - callback for an error response
*/
setAffiliations (members) { setAffiliations (members) {
/* Send IQ stanzas to the server to modify the
* affiliations in this groupchat.
*
* See: https://xmpp.org/extensions/xep-0045.html#modifymember
*
* Parameters:
* (Object) members: A map of jids, affiliations and optionally reasons
* (Function) onSuccess: callback for a succesful response
* (Function) onError: callback for an error response
*/
const affiliations = _.uniq(_.map(members, 'affiliation')); const affiliations = _.uniq(_.map(members, 'affiliation'));
return Promise.all(_.map(affiliations, _.partial(this.setAffiliation.bind(this), _, members))); return Promise.all(_.map(affiliations, _.partial(this.setAffiliation.bind(this), _, members)));
}, },
@ -787,39 +786,39 @@ converse.plugins.add('converse-muc', {
return [].concat.apply([], result).filter(p => p); return [].concat.apply([], result).filter(p => p);
}, },
/**
* Fetch the lists of users with the given affiliations.
* Then compute the delta between those users and
* the passed in members, and if it exists, send the delta
* to the XMPP server to update the member list.
* @private
* @method _converse.ChatRoom#updateMemberLists
* @param { object } members - Map of member jids and affiliations.
* @param { string|array } affiliation - An array of affiliations or
* a string if only one affiliation.
* @param { function } deltaFunc - The function to compute the delta
* between old and new member lists.
* @returns { promise }
* A promise which is resolved once the list has been
* updated or once it's been established there's no need
* to update the list.
*/
updateMemberLists (members, affiliations, deltaFunc) { updateMemberLists (members, affiliations, deltaFunc) {
/* Fetch the lists of users with the given affiliations.
* Then compute the delta between those users and
* the passed in members, and if it exists, send the delta
* to the XMPP server to update the member list.
*
* Parameters:
* (Object) members: Map of member jids and affiliations.
* (String|Array) affiliation: An array of affiliations or
* a string if only one affiliation.
* (Function) deltaFunc: The function to compute the delta
* between old and new member lists.
*
* Returns:
* A promise which is resolved once the list has been
* updated or once it's been established there's no need
* to update the list.
*/
this.getJidsWithAffiliations(affiliations) this.getJidsWithAffiliations(affiliations)
.then(old_members => this.setAffiliations(deltaFunc(members, old_members))) .then(old_members => this.setAffiliations(deltaFunc(members, old_members)))
.then(() => this.occupants.fetchMembers()) .then(() => this.occupants.fetchMembers())
.catch(_.partial(_converse.log, _, Strophe.LogLevel.ERROR)); .catch(_.partial(_converse.log, _, Strophe.LogLevel.ERROR));
}, },
/**
* Use service-discovery to ask the XMPP server whether
* this user has a reserved nickname for this groupchat.
* If so, we'll use that, otherwise we render the nickname form.
* @private
* @method _converse.ChatRoom#checkForReservedNick
* @returns { promise } A promise which resolves with the response IQ
*/
async checkForReservedNick () { async checkForReservedNick () {
/* Use service-discovery to ask the XMPP server whether
* this user has a reserved nickname for this groupchat.
* If so, we'll use that, otherwise we render the nickname form.
*
* Parameters:
* (Function) callback: Callback upon succesful IQ response
* (Function) errback: Callback upon error IQ response
*/
const iq = await _converse.api.sendIQ( const iq = await _converse.api.sendIQ(
$iq({ $iq({
'to': this.get('jid'), 'to': this.get('jid'),
@ -887,13 +886,14 @@ converse.plugins.add('converse-muc', {
} }
}, },
/**
* Given a presence stanza, update the occupant model
* based on its contents.
* @private
* @method _converse.ChatRoom#updateOccupantsOnPresence
* @param { XMLElement } pres - The presence stanza
*/
updateOccupantsOnPresence (pres) { updateOccupantsOnPresence (pres) {
/* Given a presence stanza, update the occupant model
* based on its contents.
*
* Parameters:
* (XMLElement) pres: The presence stanza
*/
const data = this.parsePresence(pres); const data = this.parsePresence(pres);
if (data.type === 'error' || (!data.jid && !data.nick)) { if (data.type === 'error' || (!data.jid && !data.nick)) {
return true; return true;
@ -989,12 +989,13 @@ converse.plugins.add('converse-muc', {
acknowledged[xmlns="${Strophe.NS.MARKERS}"]`, stanza).length > 0; acknowledged[xmlns="${Strophe.NS.MARKERS}"]`, stanza).length > 0;
}, },
/**
* Handle a subject change and return `true` if so.
* @private
* @method _converse.ChatRoom#subjectChangeHandled
* @param { object } attrs - The message attributes
*/
subjectChangeHandled (attrs) { subjectChangeHandled (attrs) {
/* Handle a subject change and return `true` if so.
*
* Parameters:
* (Object) attrs: The message attributes
*/
if (attrs.subject && !attrs.thread && !attrs.message) { if (attrs.subject && !attrs.thread && !attrs.message) {
// https://xmpp.org/extensions/xep-0045.html#subject-mod // https://xmpp.org/extensions/xep-0045.html#subject-mod
// ----------------------------------------------------- // -----------------------------------------------------
@ -1007,13 +1008,14 @@ converse.plugins.add('converse-muc', {
return false; return false;
}, },
/**
* Is this a chat state notification that can be ignored,
* because it's old or because it's from us.
* @private
* @method _converse.ChatRoom#ignorableCSN
* @param { Object } attrs - The message attributes
*/
ignorableCSN (attrs) { ignorableCSN (attrs) {
/* Is this a chat state notification that can be ignored,
* because it's old or because it's from us.
*
* Parameters:
* (Object) attrs: The message attributes
*/
const is_csn = u.isOnlyChatStateNotification(attrs), const is_csn = u.isOnlyChatStateNotification(attrs),
own_message = Strophe.getResourceFromJid(attrs.from) == this.get('nick'); own_message = Strophe.getResourceFromJid(attrs.from) == this.get('nick');
return is_csn && (attrs.is_delayed || own_message); return is_csn && (attrs.is_delayed || own_message);
@ -1038,12 +1040,13 @@ converse.plugins.add('converse-muc', {
return attrs; return attrs;
}, },
/**
* Handler for all MUC messages sent to this groupchat.
* @private
* @method _converse.ChatRoom#onMessage
* @param { XMLElement } stanza - The message stanza.
*/
async onMessage (stanza) { async onMessage (stanza) {
/* Handler for all MUC messages sent to this groupchat.
*
* Parameters:
* (XMLElement) stanza: The message stanza.
*/
this.fetchFeaturesIfConfigurationChanged(stanza); this.fetchFeaturesIfConfigurationChanged(stanza);
const original_stanza = stanza, const original_stanza = stanza,
@ -1076,12 +1079,13 @@ converse.plugins.add('converse-muc', {
_converse.api.trigger('message', {'stanza': original_stanza, 'chatbox': this}); _converse.api.trigger('message', {'stanza': original_stanza, 'chatbox': this});
}, },
/**
* Handles all MUC presence stanzas.
* @private
* @method _converse.ChatRoom#onPresence
* @param { XMLElement } pres - The stanza
*/
onPresence (pres) { onPresence (pres) {
/* Handles all MUC presence stanzas.
*
* Parameters:
* (XMLElement) pres: The stanza
*/
if (pres.getAttribute('type') === 'error') { if (pres.getAttribute('type') === 'error') {
this.save('connection_status', converse.ROOMSTATUS.DISCONNECTED); this.save('connection_status', converse.ROOMSTATUS.DISCONNECTED);
return; return;
@ -1096,22 +1100,22 @@ converse.plugins.add('converse-muc', {
} }
}, },
/**
* Handles a received presence relating to the current user.
*
* For locked groupchats (which are by definition "new"), the
* groupchat will either be auto-configured or created instantly
* (with default config) or a configuration groupchat will be
* rendered.
*
* If the groupchat is not locked, then the groupchat will be
* auto-configured only if applicable and if the current
* user is the groupchat's owner.
* @private
* @method _converse.ChatRoom#onOwnPresence
* @param { XMLElement } pres - The stanza
*/
onOwnPresence (pres) { onOwnPresence (pres) {
/* Handles a received presence relating to the current
* user.
*
* For locked groupchats (which are by definition "new"), the
* groupchat will either be auto-configured or created instantly
* (with default config) or a configuration groupchat will be
* rendered.
*
* If the groupchat is not locked, then the groupchat will be
* auto-configured only if applicable and if the current
* user is the groupchat's owner.
*
* Parameters:
* (XMLElement) pres: The stanza
*/
this.saveAffiliationAndRole(pres); this.saveAffiliationAndRole(pres);
const locked_room = pres.querySelector("status[code='201']"); const locked_room = pres.querySelector("status[code='201']");
@ -1148,13 +1152,14 @@ converse.plugins.add('converse-muc', {
this.save('connection_status', converse.ROOMSTATUS.ENTERED); this.save('connection_status', converse.ROOMSTATUS.ENTERED);
}, },
/**
* Returns a boolean to indicate whether the current user
* was mentioned in a message.
* @private
* @method _converse.ChatRoom#isUserMentioned
* @param { String } - The text message
*/
isUserMentioned (message) { isUserMentioned (message) {
/* Returns a boolean to indicate whether the current user
* was mentioned in a message.
*
* Parameters:
* (String): The text message
*/
const nick = this.get('nick'); const nick = this.get('nick');
if (message.get('references').length) { if (message.get('references').length) {
const mentions = message.get('references').filter(ref => (ref.type === 'mention')).map(ref => ref.value); const mentions = message.get('references').filter(ref => (ref.type === 'mention')).map(ref => ref.value);
@ -1164,13 +1169,12 @@ converse.plugins.add('converse-muc', {
} }
}, },
/* Given a newly received message, update the unread counter if necessary.
* @private
* @method _converse.ChatRoom#incrementUnreadMsgCounter
* @param { XMLElement } - The <messsage> stanza
*/
incrementUnreadMsgCounter (message) { incrementUnreadMsgCounter (message) {
/* Given a newly received message, update the unread counter if
* necessary.
*
* Parameters:
* (XMLElement): The <messsage> stanza
*/
if (!message) { return; } if (!message) { return; }
const body = message.get('message'); const body = message.get('message');
if (_.isNil(body)) { return; } if (_.isNil(body)) { return; }
@ -1311,14 +1315,14 @@ converse.plugins.add('converse-muc', {
}); });
/**
* A direct MUC invitation to join a groupchat has been received
* See XEP-0249: Direct MUC invitations.
* @private
* @method _converse.ChatRoom#onDirectMUCInvitation
* @param { XMLElement } message - The message stanza containing the invitation.
*/
_converse.onDirectMUCInvitation = function (message) { _converse.onDirectMUCInvitation = function (message) {
/* A direct MUC invitation to join a groupchat has been received
* See XEP-0249: Direct MUC invitations.
*
* Parameters:
* (XMLElement) message: The message stanza containing the
* invitation.
*/
const x_el = sizzle('x[xmlns="jabber:x:conference"]', message).pop(), const x_el = sizzle('x[xmlns="jabber:x:conference"]', message).pop(),
from = Strophe.getBareJidFromJid(message.getAttribute('from')), from = Strophe.getBareJidFromJid(message.getAttribute('from')),
room_jid = x_el.getAttribute('jid'), room_jid = x_el.getAttribute('jid'),

View File

@ -45,10 +45,13 @@ converse.plugins.add('converse-roster', {
}; };
/**
* Initialize the Bakcbone collections that represent the contats
* roster and the roster groups.
* @private
* @method _converse.initRoster
*/
_converse.initRoster = function () { _converse.initRoster = function () {
/* Initialize the Bakcbone collections that represent the contats
* roster and the roster groups.
*/
const storage = _converse.config.get('storage'); const storage = _converse.config.get('storage');
_converse.roster = new _converse.RosterContacts(); _converse.roster = new _converse.RosterContacts();
_converse.roster.browserStorage = new Backbone.BrowserStorage[storage]( _converse.roster.browserStorage = new Backbone.BrowserStorage[storage](
@ -75,15 +78,16 @@ converse.plugins.add('converse-roster', {
}; };
/**
* Fetch all the roster groups, and then the roster contacts.
* Emit an event after fetching is done in each case.
* @private
* @method _converse.populateRoster
* @param { Bool } ignore_cache - If set to to true, the local cache
* will be ignored it's guaranteed that the XMPP server
* will be queried for the roster.
*/
_converse.populateRoster = async function (ignore_cache=false) { _converse.populateRoster = async function (ignore_cache=false) {
/* Fetch all the roster groups, and then the roster contacts.
* Emit an event after fetching is done in each case.
*
* Parameters:
* (Bool) ignore_cache - If set to to true, the local cache
* will be ignored it's guaranteed that the XMPP server
* will be queried for the roster.
*/
if (ignore_cache) { if (ignore_cache) {
_converse.send_initial_presence = true; _converse.send_initial_presence = true;
try { try {
@ -272,13 +276,14 @@ converse.plugins.add('converse-roster', {
return this.vcard.get('fullname'); return this.vcard.get('fullname');
}, },
/**
* Send a presence subscription request to this roster contact
* @private
* @method _converse.RosterContacts#subscribe
* @param { String } message - An optional message to explain the
* reason for the subscription request.
*/
subscribe (message) { subscribe (message) {
/* Send a presence subscription request to this roster contact
*
* Parameters:
* (String) message - An optional message to explain the
* reason for the subscription request.
*/
const pres = $pres({to: this.get('jid'), type: "subscribe"}); const pres = $pres({to: this.get('jid'), type: "subscribe"});
if (message && message !== "") { if (message && message !== "") {
pres.c("status").t(message).up(); pres.c("status").t(message).up();
@ -292,46 +297,55 @@ converse.plugins.add('converse-roster', {
return this; return this;
}, },
/**
* Upon receiving the presence stanza of type "subscribed",
* the user SHOULD acknowledge receipt of that subscription
* state notification by sending a presence stanza of type
* "subscribe" to the contact
* @private
* @method _converse.RosterContacts#ackSubscribe
*/
ackSubscribe () { ackSubscribe () {
/* Upon receiving the presence stanza of type "subscribed",
* the user SHOULD acknowledge receipt of that subscription
* state notification by sending a presence stanza of type
* "subscribe" to the contact
*/
_converse.api.send($pres({ _converse.api.send($pres({
'type': 'subscribe', 'type': 'subscribe',
'to': this.get('jid') 'to': this.get('jid')
})); }));
}, },
/**
* Upon receiving the presence stanza of type "unsubscribed",
* the user SHOULD acknowledge receipt of that subscription state
* notification by sending a presence stanza of type "unsubscribe"
* this step lets the user's server know that it MUST no longer
* send notification of the subscription state change to the user.
* @private
* @method _converse.RosterContacts#ackUnsubscribe
* @param { String } jid - The Jabber ID of the user who is unsubscribing
*/
ackUnsubscribe () { ackUnsubscribe () {
/* Upon receiving the presence stanza of type "unsubscribed",
* the user SHOULD acknowledge receipt of that subscription state
* notification by sending a presence stanza of type "unsubscribe"
* this step lets the user's server know that it MUST no longer
* send notification of the subscription state change to the user.
* Parameters:
* (String) jid - The Jabber ID of the user who is unsubscribing
*/
_converse.api.send($pres({'type': 'unsubscribe', 'to': this.get('jid')})); _converse.api.send($pres({'type': 'unsubscribe', 'to': this.get('jid')}));
this.removeFromRoster(); this.removeFromRoster();
this.destroy(); this.destroy();
}, },
/**
* Unauthorize this contact's presence subscription
* @private
* @method _converse.RosterContacts#unauthorize
* @param { String } message - Optional message to send to the person being unauthorized
*/
unauthorize (message) { unauthorize (message) {
/* Unauthorize this contact's presence subscription
* Parameters:
* (String) message - Optional message to send to the person being unauthorized
*/
_converse.rejectPresenceSubscription(this.get('jid'), message); _converse.rejectPresenceSubscription(this.get('jid'), message);
return this; return this;
}, },
/**
* Authorize presence subscription
* @private
* @method _converse.RosterContacts#authorize
* @param { String } message - Optional message to send to the person being authorized
*/
authorize (message) { authorize (message) {
/* Authorize presence subscription
* Parameters:
* (String) message - Optional message to send to the person being authorized
*/
const pres = $pres({'to': this.get('jid'), 'type': "subscribed"}); const pres = $pres({'to': this.get('jid'), 'type': "subscribed"});
if (message && message !== "") { if (message && message !== "") {
pres.c("status").t(message); pres.c("status").t(message);
@ -340,11 +354,13 @@ converse.plugins.add('converse-roster', {
return this; return this;
}, },
/**
* Instruct the XMPP server to remove this contact from our roster
* @private
* @method _converse.RosterContacts#
* @returns { Promise }
*/
removeFromRoster () { removeFromRoster () {
/* Instruct the XMPP server to remove this contact from our roster
* Parameters:
* (Function) callback
*/
const iq = $iq({type: 'set'}) const iq = $iq({type: 'set'})
.c('query', {xmlns: Strophe.NS.ROSTER}) .c('query', {xmlns: Strophe.NS.ROSTER})
.c('item', {jid: this.get('jid'), subscription: "remove"}); .c('item', {jid: this.get('jid'), subscription: "remove"});
@ -352,7 +368,11 @@ converse.plugins.add('converse-roster', {
} }
}); });
/**
* @class
* @namespace _converse.RosterContacts
* @memberOf _converse
*/
_converse.RosterContacts = Backbone.Collection.extend({ _converse.RosterContacts = Backbone.Collection.extend({
model: _converse.RosterContact, model: _converse.RosterContact,
@ -460,17 +480,18 @@ converse.plugins.add('converse-roster', {
return u.isSameBareJID(jid, _converse.connection.jid); return u.isSameBareJID(jid, _converse.connection.jid);
}, },
/**
* Add a roster contact and then once we have confirmation from
* the XMPP server we subscribe to that contact's presence updates.
* @private
* @method _converse.RosterContacts#addAndSubscribe
* @param { String } jid - The Jabber ID of the user being added and subscribed to.
* @param { String } name - The name of that user
* @param { Array.String } groups - Any roster groups the user might belong to
* @param { String } message - An optional message to explain the reason for the subscription request.
* @param { Object } attributes - Any additional attributes to be stored on the user's model.
*/
addAndSubscribe (jid, name, groups, message, attributes) { addAndSubscribe (jid, name, groups, message, attributes) {
/* Add a roster contact and then once we have confirmation from
* the XMPP server we subscribe to that contact's presence updates.
* Parameters:
* (String) jid - The Jabber ID of the user being added and subscribed to.
* (String) name - The name of that user
* (Array of Strings) groups - Any roster groups the user might belong to
* (String) message - An optional message to explain the
* reason for the subscription request.
* (Object) attributes - Any additional attributes to be stored on the user's model.
*/
const handler = (contact) => { const handler = (contact) => {
if (contact instanceof _converse.RosterContact) { if (contact instanceof _converse.RosterContact) {
contact.subscribe(message); contact.subscribe(message);
@ -479,16 +500,17 @@ converse.plugins.add('converse-roster', {
this.addContactToRoster(jid, name, groups, attributes).then(handler, handler); this.addContactToRoster(jid, name, groups, attributes).then(handler, handler);
}, },
/**
* Send an IQ stanza to the XMPP server to add a new roster contact.
* @private
* @method _converse.RosterContacts#sendContactAddIQ
* @param { String } jid - The Jabber ID of the user being added
* @param { String } name - The name of that user
* @param { Array.String } groups - Any roster groups the user might belong to
* @param { Function } callback - A function to call once the IQ is returned
* @param { Function } errback - A function to call if an error occurred
*/
sendContactAddIQ (jid, name, groups) { sendContactAddIQ (jid, name, groups) {
/* Send an IQ stanza to the XMPP server to add a new roster contact.
*
* Parameters:
* (String) jid - The Jabber ID of the user being added
* (String) name - The name of that user
* (Array of Strings) groups - Any roster groups the user might belong to
* (Function) callback - A function to call once the IQ is returned
* (Function) errback - A function to call if an error occurred
*/
name = _.isEmpty(name) ? null : name; name = _.isEmpty(name) ? null : name;
const iq = $iq({'type': 'set'}) const iq = $iq({'type': 'set'})
.c('query', {'xmlns': Strophe.NS.ROSTER}) .c('query', {'xmlns': Strophe.NS.ROSTER})
@ -497,18 +519,18 @@ converse.plugins.add('converse-roster', {
return _converse.api.sendIQ(iq); return _converse.api.sendIQ(iq);
}, },
/**
* Adds a RosterContact instance to _converse.roster and
* registers the contact on the XMPP server.
* Returns a promise which is resolved once the XMPP server has responded.
* @private
* @method _converse.RosterContacts#addContactToRoster
* @param { String } jid - The Jabber ID of the user being added and subscribed to.
* @param { String } name - The name of that user
* @param { Array.String } groups - Any roster groups the user might belong to
* @param { Object } attributes - Any additional attributes to be stored on the user's model.
*/
async addContactToRoster (jid, name, groups, attributes) { async addContactToRoster (jid, name, groups, attributes) {
/* Adds a RosterContact instance to _converse.roster and
* registers the contact on the XMPP server.
* Returns a promise which is resolved once the XMPP server has
* responded.
*
* Parameters:
* (String) jid - The Jabber ID of the user being added and subscribed to.
* (String) name - The name of that user
* (Array of Strings) groups - Any roster groups the user might belong to
* (Object) attributes - Any additional attributes to be stored on the user's model.
*/
groups = groups || []; groups = groups || [];
try { try {
await this.sendContactAddIQ(jid, name, groups); await this.sendContactAddIQ(jid, name, groups);
@ -551,13 +573,14 @@ converse.plugins.add('converse-roster', {
return _.sum(this.models.filter((model) => !_.includes(ignored, model.presence.get('show')))); return _.sum(this.models.filter((model) => !_.includes(ignored, model.presence.get('show'))));
}, },
/**
* Handle roster updates from the XMPP server.
* See: https://xmpp.org/rfcs/rfc6121.html#roster-syntax-actions-push
* @private
* @method _converse.RosterContacts#onRosterPush
* @param { XMLElement } IQ - The IQ stanza received from the XMPP server.
*/
onRosterPush (iq) { onRosterPush (iq) {
/* Handle roster updates from the XMPP server.
* See: https://xmpp.org/rfcs/rfc6121.html#roster-syntax-actions-push
*
* Parameters:
* (XMLElement) IQ - The IQ stanza received from the XMPP server.
*/
const id = iq.getAttribute('id'); const id = iq.getAttribute('id');
const from = iq.getAttribute('from'); const from = iq.getAttribute('from');
if (from && from !== _converse.bare_jid) { if (from && from !== _converse.bare_jid) {

View File

@ -100,12 +100,6 @@ converse.plugins.add('converse-vcard', {
} }
async function getVCard (_converse, jid) { async function getVCard (_converse, jid) {
/* Request the VCard of another user. Returns a promise.
*
* Parameters:
* (String) jid - The Jabber ID of the user whose VCard
* is being requested.
*/
const to = Strophe.getBareJidFromJid(jid) === _converse.bare_jid ? null : jid; const to = Strophe.getBareJidFromJid(jid) === _converse.bare_jid ? null : jid;
let iq; let iq;
try { try {

File diff suppressed because it is too large Load Diff

View File

@ -44,10 +44,8 @@ import moment from "moment";
function detectLocale (library_check) { function detectLocale (library_check) {
/* Determine which locale is supported by the user's system as well /* Determine which locale is supported by the user's system as well
* as by the relevant library (e.g. converse.js or moment.js). * as by the relevant library (e.g. converse.js or moment.js).
* * @param { Function } library_check - Returns a boolean indicating whether
* Parameters: * the locale is supported.
* (Function) library_check - Returns a boolean indicating whether
* the locale is supported.
*/ */
var locale, i; var locale, i;
if (window.navigator.userLanguage) { if (window.navigator.userLanguage) {
@ -87,13 +85,11 @@ function getLocale (preferred_locale, isSupportedByLibrary) {
return detectLocale(isSupportedByLibrary) || 'en'; return detectLocale(isSupportedByLibrary) || 'en';
} }
/* Check whether the locale or sub locale (e.g. en-US, en) is supported.
* @param { String } locale - The locale to check for
* @param { Function } available - Returns a boolean indicating whether the locale is supported
*/
function isLocaleAvailable (locale, available) { function isLocaleAvailable (locale, available) {
/* Check whether the locale or sub locale (e.g. en-US, en) is supported.
*
* Parameters:
* (String) locale - The locale to check for
* (Function) available - returns a boolean indicating whether the locale is supported
*/
if (available(locale)) { if (available(locale)) {
return locale; return locale;
} else { } else {
@ -106,6 +102,9 @@ function isLocaleAvailable (locale, available) {
let jed_instance; let jed_instance;
/**
* @namespace i18n
*/
export default { export default {
setLocales (preferred_locale, _converse) { setLocales (preferred_locale, _converse) {
@ -120,23 +119,23 @@ export default {
if (_.isNil(jed_instance)) { if (_.isNil(jed_instance)) {
return Jed.sprintf.apply(Jed, arguments); return Jed.sprintf.apply(Jed, arguments);
} }
var t = jed_instance.translate(str); const t = jed_instance.translate(str);
if (arguments.length>1) { if (arguments.length > 1) {
return t.fetch.apply(t, [].slice.call(arguments, 1)); return t.fetch.apply(t, [].slice.call(arguments, 1));
} else { } else {
return t.fetch(); return t.fetch();
} }
}, },
/**
* Fetch the translations for the given local at the given URL.
* @private
* @method i18n#fetchTranslations
* @param { String } locale -The given i18n locale
* @param { Array } supported_locales - List of locales supported
* @param { String } locale_url - The URL from which the translations should be fetched
*/
fetchTranslations (locale, supported_locales, locale_url) { fetchTranslations (locale, supported_locales, locale_url) {
/* Fetch the translations for the given local at the given URL.
*
* Parameters:
* (String) locale: The given i18n locale
* (Array) supported_locales: List of locales supported
* (String) locale_url: The URL from which the translations
* should be fetched.
*/
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
if (!isConverseLocale(locale, supported_locales) || locale === 'en') { if (!isConverseLocale(locale, supported_locales) || locale === 'en') {
return resolve(); return resolve();

View File

@ -14,6 +14,11 @@ import { Strophe } from "strophe.js";
import _ from "../lodash.noconflict"; import _ from "../lodash.noconflict";
import sizzle from "sizzle"; import sizzle from "sizzle";
/**
* The utils object
* @namespace u
*/
const u = {}; const u = {};
u.toStanza = function (string) { u.toStanza = function (string) {
@ -147,14 +152,15 @@ u.applyUserSettings = function applyUserSettings (context, settings, user_settin
} }
}; };
/**
* Converts an HTML string into a DOM Node.
* Expects that the HTML string has only one top-level element,
* i.e. not multiple ones.
* @private
* @method u#stringToNode
* @param { String } s - The HTML string
*/
u.stringToNode = function (s) { u.stringToNode = function (s) {
/* Converts an HTML string into a DOM Node.
* Expects that the HTML string has only one top-level element,
* i.e. not multiple ones.
*
* Parameters:
* (String) s - The HTML string
*/
var div = document.createElement('div'); var div = document.createElement('div');
div.innerHTML = s; div.innerHTML = s;
return div.firstElementChild; return div.firstElementChild;
@ -170,26 +176,28 @@ u.getOuterWidth = function (el, include_margin=false) {
return width; return width;
}; };
/**
* Converts an HTML string into a DOM element.
* Expects that the HTML string has only one top-level element,
* i.e. not multiple ones.
* @private
* @method u#stringToElement
* @param { String } s - The HTML string
*/
u.stringToElement = function (s) { u.stringToElement = function (s) {
/* Converts an HTML string into a DOM element.
* Expects that the HTML string has only one top-level element,
* i.e. not multiple ones.
*
* Parameters:
* (String) s - The HTML string
*/
var div = document.createElement('div'); var div = document.createElement('div');
div.innerHTML = s; div.innerHTML = s;
return div.firstElementChild; return div.firstElementChild;
}; };
/**
* Checks whether the DOM element matches the given selector.
* @private
* @method u#matchesSelector
* @param { DOMElement } el - The DOM element
* @param { String } selector - The selector
*/
u.matchesSelector = function (el, selector) { u.matchesSelector = function (el, selector) {
/* Checks whether the DOM element matches the given selector.
*
* Parameters:
* (DOMElement) el - The DOM element
* (String) selector - The selector
*/
const match = ( const match = (
el.matches || el.matches ||
el.matchesSelector || el.matchesSelector ||
@ -201,15 +209,14 @@ u.matchesSelector = function (el, selector) {
return match ? match.call(el, selector) : false; return match ? match.call(el, selector) : false;
}; };
/**
* Returns a list of children of the DOM element that match the selector.
* @private
* @method u#queryChildren
* @param { DOMElement } el - the DOM element
* @param { String } selector - the selector they should be matched against
*/
u.queryChildren = function (el, selector) { u.queryChildren = function (el, selector) {
/* Returns a list of children of the DOM element that match the
* selector.
*
* Parameters:
* (DOMElement) el - the DOM element
* (String) selector - the selector they should be matched
* against.
*/
return _.filter(el.childNodes, _.partial(u.matchesSelector, _, selector)); return _.filter(el.childNodes, _.partial(u.matchesSelector, _, selector));
}; };
@ -296,16 +303,17 @@ u.interpolate = function (string, o) {
}); });
}; };
/**
* Call the callback once all the events have been triggered
* @private
* @method u#onMultipleEvents
* @param { Array } events: An array of objects, with keys `object` and
* `event`, representing the event name and the object it's
* triggered upon.
* @param { Function } callback: The function to call once all events have
* been triggered.
*/
u.onMultipleEvents = function (events=[], callback) { u.onMultipleEvents = function (events=[], callback) {
/* Call the callback once all the events have been triggered
*
* Parameters:
* (Array) events: An array of objects, with keys `object` and
* `event`, representing the event name and the object it's
* triggered upon.
* (Function) callback: The function to call once all events have
* been triggered.
*/
let triggered = []; let triggered = [];
function handler (result) { function handler (result) {

View File

@ -10,12 +10,13 @@ import _ from "../lodash.noconflict";
import tpl_field from "../templates/field.html"; import tpl_field from "../templates/field.html";
import u from "./core"; import u from "./core";
/**
* Takes an HTML DOM and turns it into an XForm field.
* @private
* @method u#webForm2xForm
* @param { DOMElement } field - the field to convert
*/
u.webForm2xForm = function (field) { u.webForm2xForm = function (field) {
/* Takes an HTML DOM and turns it into an XForm field.
*
* Parameters:
* (DOMElement) field - the field to convert
*/
let value; let value;
if (field.getAttribute('type') === 'checkbox') { if (field.getAttribute('type') === 'checkbox') {
value = field.checked && 1 || 0; value = field.checked && 1 || 0;

View File

@ -15,33 +15,34 @@ import u from "./core";
const { Strophe, sizzle, _ } = converse.env; const { Strophe, sizzle, _ } = converse.env;
/**
* Given two lists of objects with 'jid', 'affiliation' and
* 'reason' properties, return a new list containing
* those objects that are new, changed or removed
* (depending on the 'remove_absentees' boolean).
*
* The affiliations for new and changed members stay the
* same, for removed members, the affiliation is set to 'none'.
*
* The 'reason' property is not taken into account when
* comparing whether affiliations have been changed.
* @private
* @method u#computeAffiliationsDelta
* @param { boolean } exclude_existing - Indicates whether JIDs from
* the new list which are also in the old list
* (regardless of affiliation) should be excluded
* from the delta. One reason to do this
* would be when you want to add a JID only if it
* doesn't have *any* existing affiliation at all.
* @param { boolean } remove_absentees - Indicates whether JIDs
* from the old list which are not in the new list
* should be considered removed and therefore be
* included in the delta with affiliation set
* to 'none'.
* @param { array } new_list - Array containing the new affiliations
* @param { array } old_list - Array containing the old affiliations
*/
u.computeAffiliationsDelta = function computeAffiliationsDelta (exclude_existing, remove_absentees, new_list, old_list) { u.computeAffiliationsDelta = function computeAffiliationsDelta (exclude_existing, remove_absentees, new_list, old_list) {
/* Given two lists of objects with 'jid', 'affiliation' and
* 'reason' properties, return a new list containing
* those objects that are new, changed or removed
* (depending on the 'remove_absentees' boolean).
*
* The affiliations for new and changed members stay the
* same, for removed members, the affiliation is set to 'none'.
*
* The 'reason' property is not taken into account when
* comparing whether affiliations have been changed.
*
* Parameters:
* (Boolean) exclude_existing: Indicates whether JIDs from
* the new list which are also in the old list
* (regardless of affiliation) should be excluded
* from the delta. One reason to do this
* would be when you want to add a JID only if it
* doesn't have *any* existing affiliation at all.
* (Boolean) remove_absentees: Indicates whether JIDs
* from the old list which are not in the new list
* should be considered removed and therefore be
* included in the delta with affiliation set
* to 'none'.
* (Array) new_list: Array containing the new affiliations
* (Array) old_list: Array containing the old affiliations
*/
const new_jids = _.map(new_list, 'jid'); const new_jids = _.map(new_list, 'jid');
const old_jids = _.map(old_list, 'jid'); const old_jids = _.map(old_list, 'jid');

View File

@ -304,13 +304,14 @@ u.nextUntil = function (el, selector, include_self=false) {
return matches; return matches;
} }
/**
* Helper method that replace HTML-escaped symbols with equivalent characters
* (e.g. transform occurrences of '&amp;' to '&')
* @private
* @method u#unescapeHTML
* @param { String } string - a String containing the HTML-escaped symbols.
*/
u.unescapeHTML = function (string) { u.unescapeHTML = function (string) {
/* Helper method that replace HTML-escaped symbols with equivalent characters
* (e.g. transform occurrences of '&amp;' to '&')
*
* Parameters:
* (String) string: a String containing the HTML-escaped symbols.
*/
var div = document.createElement('div'); var div = document.createElement('div');
div.innerHTML = string; div.innerHTML = string;
return div.innerText; return div.innerText;
@ -381,13 +382,14 @@ u.slideToggleElement = function (el, duration) {
}; };
/**
* Shows/expands an element by sliding it out of itself
* @private
* @method u#slideOut
* @param { HTMLElement } el - The HTML string
* @param { Number } duration - The duration amount in milliseconds
*/
u.slideOut = function (el, duration=200) { u.slideOut = function (el, duration=200) {
/* Shows/expands an element by sliding it out of itself
*
* Parameters:
* (HTMLElement) el - The HTML string
* (Number) duration - The duration amount in milliseconds
*/
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
if (_.isNil(el)) { if (_.isNil(el)) {
const err = "Undefined or null element passed into slideOut" const err = "Undefined or null element passed into slideOut"
@ -528,16 +530,15 @@ u.fadeIn = function (el, callback) {
}; };
/**
* Takes a field in XMPP XForm (XEP-004: Data Forms) format
* and turns it into an HTML field.
* Returns either text or a DOM element (which is not ideal, but fine for now).
* @private
* @method u#xForm2webForm
* @param { XMLElement } field - the field to convert
*/
u.xForm2webForm = function (field, stanza, domain) { u.xForm2webForm = function (field, stanza, domain) {
/* Takes a field in XMPP XForm (XEP-004: Data Forms) format
* and turns it into an HTML field.
*
* Returns either text or a DOM element (which is not ideal, but fine
* for now).
*
* Parameters:
* (XMLElement) field - the field to convert
*/
if (field.getAttribute('type') === 'list-single' || if (field.getAttribute('type') === 'list-single' ||
field.getAttribute('type') === 'list-multi') { field.getAttribute('type') === 'list-multi') {