core: refactor initialize method to make it as small as possible

This commit is contained in:
JC Brand 2020-03-31 12:28:11 +02:00
parent 631b9bb438
commit 8b1d4e0e9d

View File

@ -559,9 +559,8 @@ async function onDomainDiscovered (response) {
}
/* Use XEP-0156 to check whether this host advertises websocket or BOSH connection methods.
*/
async function discoverConnectionMethods (domain) {
// Use XEP-0156 to check whether this host advertises websocket or BOSH connection methods.
const options = {
'mode': 'cors',
'headers': {
@ -954,230 +953,154 @@ function cleanup () {
_converse.off();
}
_converse.generateResource = () => `/converse.js-${Math.floor(Math.random()*139749528).toString()}`;
_converse.initialize = async function (settings, callback) {
cleanup();
settings = settings !== undefined ? settings : {};
PROMISES.forEach(name => _converse.api.promises.add(name));
if ('onpagehide' in window) {
// Pagehide gets thrown in more cases than unload. Specifically it
// gets thrown when the page is cached and not just
// closed/destroyed. It's the only viable event on mobile Safari.
// https://www.webkit.org/blog/516/webkit-page-cache-ii-the-unload-event/
_converse.unloadevent = 'pagehide';
} else if ('onbeforeunload' in window) {
_converse.unloadevent = 'beforeunload';
} else if ('onunload' in window) {
_converse.unloadevent = 'unload';
}
this.settings = {};
assignIn(this.settings, DEFAULT_SETTINGS);
// Allow only whitelisted configuration attributes to be overwritten
assignIn(this.settings, pick(settings, Object.keys(DEFAULT_SETTINGS)));
assignIn(this, this.settings);
this.user_settings = settings; // XXX: See whether this can be removed
// Needed by pluggable.js
this.strict_plugin_dependencies = settings.strict_plugin_dependencies;
log.setLogLevel(_converse.api.settings.get("loglevel"));
_converse.log = log.log;
if (_converse.api.settings.get("authentication") === _converse.ANONYMOUS) {
if (_converse.api.settings.get("auto_login") && !this.jid) {
throw new Error("Config Error: you need to provide the server's " +
"domain via the 'jid' option when using anonymous " +
"authentication with auto_login.");
}
}
_converse.router.route(
/^converse\?loglevel=(debug|info|warn|error|fatal)$/, 'loglevel',
l => log.setLogLevel(l)
);
/* Localisation */
if (_converse.isTestEnv()) {
_converse.locale = 'en';
} else {
try {
_converse.locale = i18n.getLocale(settings.i18n, _converse.api.settings.get("locales"));
await i18n.fetchTranslations(_converse);
} catch (e) {
log.fatal(e.message);
_converse.locale = 'en';
}
}
// Module-level variables
// ----------------------
this.callback = callback || function noop () {};
/* When reloading the page:
* For new sessions, we need to send out a presence stanza to notify
* the server/network that we're online.
* When re-attaching to an existing session we don't need to again send out a presence stanza,
* because it's as if "we never left" (see onConnectStatusChanged).
* https://github.com/jcbrand/converse.js/issues/521
*/
this.send_initial_presence = true;
// Module-level functions
// ----------------------
this.generateResource = () => `/converse.js-${Math.floor(Math.random()*139749528).toString()}`;
this.setConnectionStatus = function (connection_status, message) {
_converse.connfeedback.set({
'connection_status': connection_status,
'message': message
});
};
/**
* Gets called once strophe's status reaches Strophe.Status.DISCONNECTED.
* Will either start a teardown process for converse.js or attempt
* to reconnect.
* @method onDisconnected
* @private
* @memberOf _converse
*/
this.onDisconnected = function () {
const reason = _converse.disconnection_reason;
if (_converse.disconnection_cause === Strophe.Status.AUTHFAIL) {
if (_converse.api.settings.get("auto_reconnect") &&
(_converse.api.settings.get("credentials_url") || _converse.api.settings.get("authentication") === _converse.ANONYMOUS)) {
/**
* If `credentials_url` is set, we reconnect, because we might
* be receiving expirable tokens from the credentials_url.
*
* If `authentication` is anonymous, we reconnect because we
* might have tried to attach with stale BOSH session tokens
* or with a cached JID and password
*/
return _converse.api.connection.reconnect();
} else {
return finishDisconnection();
}
} else if (_converse.disconnection_cause === _converse.LOGOUT ||
(reason !== undefined && reason === Strophe?.ErrorCondition.NO_AUTH_MECH) ||
reason === "host-unknown" ||
reason === "remote-connection-failed" ||
!_converse.api.settings.get("auto_reconnect")) {
/**
* Gets called once strophe's status reaches Strophe.Status.DISCONNECTED.
* Will either start a teardown process for converse.js or attempt
* to reconnect.
* @method onDisconnected
* @private
* @memberOf _converse
*/
_converse.onDisconnected = function () {
const reason = _converse.disconnection_reason;
if (_converse.disconnection_cause === Strophe.Status.AUTHFAIL) {
if (_converse.api.settings.get("auto_reconnect") &&
(_converse.api.settings.get("credentials_url") || _converse.api.settings.get("authentication") === _converse.ANONYMOUS)) {
/**
* If `credentials_url` is set, we reconnect, because we might
* be receiving expirable tokens from the credentials_url.
*
* If `authentication` is anonymous, we reconnect because we
* might have tried to attach with stale BOSH session tokens
* or with a cached JID and password
*/
return _converse.api.connection.reconnect();
} else {
return finishDisconnection();
}
_converse.api.connection.reconnect();
};
} else if (_converse.disconnection_cause === _converse.LOGOUT ||
(reason !== undefined && reason === Strophe?.ErrorCondition.NO_AUTH_MECH) ||
reason === "host-unknown" ||
reason === "remote-connection-failed" ||
!_converse.api.settings.get("auto_reconnect")) {
return finishDisconnection();
}
_converse.api.connection.reconnect();
};
this.setDisconnectionCause = function (cause, reason, override) {
/* Used to keep track of why we got disconnected, so that we can
* decide on what the next appropriate action is (in onDisconnected)
*/
if (cause === undefined) {
delete _converse.disconnection_cause;
delete _converse.disconnection_reason;
} else if (_converse.disconnection_cause === undefined || override) {
_converse.disconnection_cause = cause;
_converse.disconnection_reason = reason;
}
};
/**
* Callback method called by Strophe as the Strophe.Connection goes
* through various states while establishing or tearing down a
* connection.
* @method _converse#onConnectStatusChanged
* @private
* @memberOf _converse
*/
this.onConnectStatusChanged = function (status, message) {
log.debug(`Status changed to: ${_converse.CONNECTION_STATUS[status]}`);
if (status === Strophe.Status.CONNECTED || status === Strophe.Status.ATTACHED) {
_converse.setConnectionStatus(status);
// By default we always want to send out an initial presence stanza.
_converse.send_initial_presence = true;
_converse.setDisconnectionCause();
if (_converse.connection.reconnecting) {
log.debug(status === Strophe.Status.CONNECTED ? 'Reconnected' : 'Reattached');
onConnected(true);
} else {
log.debug(status === Strophe.Status.CONNECTED ? 'Connected' : 'Attached');
if (_converse.connection.restored) {
// No need to send an initial presence stanza when
// we're restoring an existing session.
_converse.send_initial_presence = false;
}
onConnected();
/**
* Callback method called by Strophe as the Strophe.Connection goes
* through various states while establishing or tearing down a
* connection.
* @method _converse#onConnectStatusChanged
* @private
* @memberOf _converse
*/
_converse.onConnectStatusChanged = function (status, message) {
log.debug(`Status changed to: ${_converse.CONNECTION_STATUS[status]}`);
if (status === Strophe.Status.CONNECTED || status === Strophe.Status.ATTACHED) {
_converse.setConnectionStatus(status);
// By default we always want to send out an initial presence stanza.
_converse.send_initial_presence = true;
_converse.setDisconnectionCause();
if (_converse.connection.reconnecting) {
log.debug(status === Strophe.Status.CONNECTED ? 'Reconnected' : 'Reattached');
onConnected(true);
} else {
log.debug(status === Strophe.Status.CONNECTED ? 'Connected' : 'Attached');
if (_converse.connection.restored) {
// No need to send an initial presence stanza when
// we're restoring an existing session.
_converse.send_initial_presence = false;
}
} else if (status === Strophe.Status.DISCONNECTED) {
_converse.setDisconnectionCause(status, message);
_converse.onDisconnected();
} else if (status === Strophe.Status.BINDREQUIRED) {
_converse.bindResource();
} else if (status === Strophe.Status.ERROR) {
_converse.setConnectionStatus(
status,
__('An error occurred while connecting to the chat server.')
);
} else if (status === Strophe.Status.CONNECTING) {
_converse.setConnectionStatus(status);
} else if (status === Strophe.Status.AUTHENTICATING) {
_converse.setConnectionStatus(status);
} else if (status === Strophe.Status.AUTHFAIL) {
if (!message) {
message = __('Your XMPP address and/or password is incorrect. Please try again.');
}
_converse.setConnectionStatus(status, message);
_converse.setDisconnectionCause(status, message, true);
_converse.onDisconnected();
} else if (status === Strophe.Status.CONNFAIL) {
let feedback = message;
if (message === "host-unknown" || message == "remote-connection-failed") {
feedback = __("Sorry, we could not connect to the XMPP host with domain: %1$s",
`\"${Strophe.getDomainFromJid(_converse.connection.jid)}\"`);
} else if (message !== undefined && message === Strophe?.ErrorCondition?.NO_AUTH_MECH) {
feedback = __("The XMPP server did not offer a supported authentication mechanism");
}
_converse.setConnectionStatus(status, feedback);
_converse.setDisconnectionCause(status, message);
} else if (status === Strophe.Status.DISCONNECTING) {
_converse.setDisconnectionCause(status, message);
onConnected();
}
};
this.bindResource = async function () {
/**
* Synchronous event triggered before we send an IQ to bind the user's
* JID resource for this session.
* @event _converse#beforeResourceBinding
*/
await _converse.api.trigger('beforeResourceBinding', {'synchronous': true});
_converse.connection.bind();
};
this.ConnectionFeedback = Model.extend({
defaults: {
'connection_status': Strophe.Status.DISCONNECTED,
'message': ''
},
initialize () {
this.on('change', () => _converse.api.trigger('connfeedback', _converse.connfeedback));
} else if (status === Strophe.Status.DISCONNECTED) {
_converse.setDisconnectionCause(status, message);
_converse.onDisconnected();
} else if (status === Strophe.Status.BINDREQUIRED) {
_converse.bindResource();
} else if (status === Strophe.Status.ERROR) {
_converse.setConnectionStatus(
status,
__('An error occurred while connecting to the chat server.')
);
} else if (status === Strophe.Status.CONNECTING) {
_converse.setConnectionStatus(status);
} else if (status === Strophe.Status.AUTHENTICATING) {
_converse.setConnectionStatus(status);
} else if (status === Strophe.Status.AUTHFAIL) {
if (!message) {
message = __('Your XMPP address and/or password is incorrect. Please try again.');
}
});
this.connfeedback = new this.ConnectionFeedback();
// Initialization
// --------------
await finishInitialization();
if (_converse.isTestEnv()) {
return _converse;
_converse.setConnectionStatus(status, message);
_converse.setDisconnectionCause(status, message, true);
_converse.onDisconnected();
} else if (status === Strophe.Status.CONNFAIL) {
let feedback = message;
if (message === "host-unknown" || message == "remote-connection-failed") {
feedback = __("Sorry, we could not connect to the XMPP host with domain: %1$s",
`\"${Strophe.getDomainFromJid(_converse.connection.jid)}\"`);
} else if (message !== undefined && message === Strophe?.ErrorCondition?.NO_AUTH_MECH) {
feedback = __("The XMPP server did not offer a supported authentication mechanism");
}
_converse.setConnectionStatus(status, feedback);
_converse.setDisconnectionCause(status, message);
} else if (status === Strophe.Status.DISCONNECTING) {
_converse.setDisconnectionCause(status, message);
}
};
_converse.setConnectionStatus = function (connection_status, message) {
_converse.connfeedback.set({
'connection_status': connection_status,
'message': message
});
};
/**
* Used to keep track of why we got disconnected, so that we can
* decide on what the next appropriate action is (in onDisconnected)
*/
_converse.setDisconnectionCause = function (cause, reason, override) {
if (cause === undefined) {
delete _converse.disconnection_cause;
delete _converse.disconnection_reason;
} else if (_converse.disconnection_cause === undefined || override) {
_converse.disconnection_cause = cause;
_converse.disconnection_reason = reason;
}
};
_converse.bindResource = async function () {
/**
* Synchronous event triggered before we send an IQ to bind the user's
* JID resource for this session.
* @event _converse#beforeResourceBinding
*/
await _converse.api.trigger('beforeResourceBinding', {'synchronous': true});
_converse.connection.bind();
};
_converse.ConnectionFeedback = Model.extend({
defaults: {
'connection_status': Strophe.Status.DISCONNECTED,
'message': ''
},
initialize () {
this.on('change', () => _converse.api.trigger('connfeedback', _converse.connfeedback));
}
});
/**
* ### The private API
*
@ -1191,7 +1114,7 @@ _converse.initialize = async function (settings, callback) {
* @namespace _converse.api
* @memberOf _converse
*/
_converse.api = {
const api = _converse.api = {
/**
* This grouping collects API functions related to the XMPP connection.
*
@ -1682,6 +1605,46 @@ _converse.api = {
};
async function initLocale () {
if (_converse.isTestEnv()) {
_converse.locale = 'en';
} else {
try {
_converse.locale = i18n.getLocale(api.settings.get('i18n'), api.settings.get("locales"));
await i18n.fetchTranslations(_converse);
} catch (e) {
log.fatal(e.message);
_converse.locale = 'en';
}
}
}
function initSettings (settings) {
_converse.settings = {};
assignIn(_converse.settings, DEFAULT_SETTINGS);
// Allow only whitelisted configuration attributes to be overwritten
assignIn(_converse.settings, pick(settings, Object.keys(DEFAULT_SETTINGS)));
assignIn(_converse, _converse.settings);
_converse.user_settings = settings; // XXX: See whether _converse can be removed
}
function setUnloadEvent () {
if ('onpagehide' in window) {
// Pagehide gets thrown in more cases than unload. Specifically it
// gets thrown when the page is cached and not just
// closed/destroyed. It's the only viable event on mobile Safari.
// https://www.webkit.org/blog/516/webkit-page-cache-ii-the-unload-event/
_converse.unloadevent = 'pagehide';
} else if ('onbeforeunload' in window) {
_converse.unloadevent = 'beforeunload';
} else if ('onunload' in window) {
_converse.unloadevent = 'unload';
}
}
window.converse = window.converse || {};
/**
@ -1721,6 +1684,7 @@ Object.assign(window.converse, {
/**
* Public API method which initializes Converse.
* This method must always be called when using Converse.
* @async
* @memberOf converse
* @method initialize
* @param {object} config A map of [configuration-settings](https://conversejs.org/docs/html/configuration.html#configuration-settings).
@ -1737,9 +1701,43 @@ Object.assign(window.converse, {
* roster_groups: true
* });
*/
initialize (settings, callback) {
return _converse.initialize(settings, callback);
async initialize (settings) {
cleanup();
PROMISES.forEach(name => api.promises.add(name));
setUnloadEvent();
initSettings(settings);
_converse.strict_plugin_dependencies = settings.strict_plugin_dependencies; // Needed by pluggable.js
log.setLogLevel(api.settings.get("loglevel"));
if (api.settings.get("authentication") === _converse.ANONYMOUS) {
if (api.settings.get("auto_login") && !api.settings.get('jid')) {
throw new Error("Config Error: you need to provide the server's " +
"domain via the 'jid' option when using anonymous " +
"authentication with auto_login.");
}
}
_converse.router.route(
/^converse\?loglevel=(debug|info|warn|error|fatal)$/, 'loglevel',
l => log.setLogLevel(l)
);
initLocale();
_converse.connfeedback = new _converse.ConnectionFeedback();
/* When reloading the page:
* For new sessions, we need to send out a presence stanza to notify
* the server/network that we're online.
* When re-attaching to an existing session we don't need to again send out a presence stanza,
* because it's as if "we never left" (see onConnectStatusChanged).
* https://github.com/conversejs/converse.js/issues/521
*/
_converse.send_initial_presence = true;
await finishInitialization();
if (_converse.isTestEnv()) {
return _converse;
}
},
/**
* Exposes methods for adding and removing plugins. You'll need to write a plugin
* if you want to have access to the private API methods defined further down below.