Commit e4f04f7b authored by JC Brand's avatar JC Brand

Update plugin docs.

- Remove section on UMD
- Add section explaining how to override templates
- Add overrides example to the relevant section
- Mention composition to add methods to existing classes
parent aece9562
......@@ -10,30 +10,30 @@ Writing a plugin
Introduction
------------
Converse.js exposes a plugin architecture through which developers can modify
Converse exposes a plugin architecture through which developers can modify
and extend its functionality.
Using plugins is good engineering practice, and using them is the *only* recommended
way of changing converse.js or adding new features to it.
way of changing Converse or adding new features to it.
In particular, plugins have the following advantages:
The main benefit of plugins is their *isolation of concerns* (and features).
From this benefit flows various 2nd degree advantages, such as the ability to
make smaller production builds (by excluding unused plugins) and an easier
upgrade path by avoiding touching converse.js's internals.
upgrade path by avoiding touching Converse's internals.
Each plugin comes in its own file, and converse.js's plugin architecture,
Each plugin comes in its own file, and Converse's plugin architecture,
called `pluggable.js <https://github.com/jcbrand/pluggable.js/>`_, provides you
with the ability to "hook in" to the core code and other plugins.
Converse.js itself is composed out of plugins and uses pluggable.js. Take a look at the
Converse itself is composed out of plugins and uses pluggable.js. Take a look at the
`src <https://github.com/jcbrand/converse.js/tree/master/src>`_ directory. All
the files that follow the pattern `converse-*.js` are plugins.
Plugins (by way of Pluggable.js) enable developers to extend and override existing objects,
functions and the `Backbone <http://backbonejs.org/>`_ models and views that make up
Converse.js.
Converse.
Besides that, in plugins you can also write new Backbone (or other) models and views,
in order to add a new functionality.
......@@ -45,16 +45,16 @@ and to understand its inner workings, please refer to the `annotated source code
.. note:: **Trying out a plugin in JSFiddle**
Because Converse.js consists only of JavaScript, HTML and CSS (with no backend
Because Converse consists only of JavaScript, HTML and CSS (with no backend
code required like PHP, Python or Ruby) it runs fine in JSFiddle.
Here's a Fiddle with a Converse.js plugin that calls ``alert`` once it gets
Here's a Fiddle with a Converse plugin that calls ``alert`` once it gets
initialized and also when a chat message gets rendered: https://jsfiddle.net/4drfaok0/15/
.. note:: **Generating a plugin with Yeoman**
The rest of this document explains how to write a plugin for Converse.js and
The rest of this document explains how to write a plugin for Converse and
ends with a documented example of a plugin.
There is a `Yeoman <http://yeoman.io/>`_ code generator, called
......@@ -71,12 +71,12 @@ Registering a plugin
Plugins need to be registered (and whitelisted) before they can be loaded and
initialized.
You register a converse.js plugin by calling ``converse.plugins.add``.
You register a Converse plugin by calling ``converse.plugins.add``.
The plugin itself is a JavaScript object which usually has at least an
``initialize`` method, which gets called at the end of the
``converse.initialize`` method which is the top-level method that gets called
by the website to configure and initialize Converse.js itself.
by the website to configure and initialize Converse itself.
Here's an example code snippet:
......@@ -101,7 +101,7 @@ Here's an example code snippet:
Whitelisting of plugins
-----------------------
As of converse.js 3.0.0 and higher, plugins need to be whitelisted before they
As of Converse 3.0.0 and higher, plugins need to be whitelisted before they
can be used. This is because plugins have access to a powerful API. For
example, they can read all messages and send messages on the user's behalf.
......@@ -112,7 +112,7 @@ To whitelist a plugin simply means to specify :ref:`whitelisted_plugins` when
you call ``converse.initialize``.
If you're adding a "core" plugin, which means a plugin that will be
included in the default, open-source version of converse.js, then you'll
included in the default, open-source version of Converse, then you'll
instead whitelist the plugin by adding its name to the `core_plugins` array in
`./src/headless/converse-core.js <https://github.com/jcbrand/converse.js/blob/master/src/headless/converse-core.js>`_.
or the `WHITELISTED_PLUGINS` array in `./src/converse.js <https://github.com/jcbrand/converse.js/blob/master/src/converse.js>`_.
......@@ -138,43 +138,6 @@ The inner ``_converse`` object is made private in order to safely hide and
encapsulate sensitive information and methods which should not be exposed
to any 3rd-party scripts that might be running in the same page.
Loading a plugin module
-----------------------
Converse.js uses the UMD (Universal Modules Definition) as its module syntax.
This makes modules loadable via `require.js`, `webpack` or other module
loaders, but also includable as old-school `<script>` tags in your HTML.
Here's an example of the plugin shown above wrapped inside a UMD module:
.. code-block:: javascript
(function (root, factory) {
if (typeof define === 'function' && define.amd) {
// AMD. Register as a module called "myplugin"
define(["converse"], factory);
} else {
// Browser globals. If you're not using a module loader such as require.js,
// then this line below executes. Make sure that your plugin's <script> tag
// appears after the one from converse.js.
factory(converse);
}
}(this, function (converse) {
converse.plugins.add('myplugin', {
initialize: function () {
// This method gets called once converse.initialize has been called
// and the plugin itself has been loaded.
// Inside this method, you have access to the closured
// _converse object as an attribute on "this".
// E.g. this._converse
},
});
});
Accessing 3rd party libraries
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
......@@ -182,20 +145,13 @@ Accessing 3rd party libraries
Immediately inside the module shown above you can access 3rd party libraries (such
dayjs and lodash) via the ``converse.env`` map.
The code for it would look something like this:
The code for it could look something like this:
.. code-block:: javascript
// Commonly used utilities and variables can be found under the "env"
// namespace of the "converse" global.
const Strophe = converse.env.Strophe,
$iq = converse.env.$iq,
$msg = converse.env.$msg,
$pres = converse.env.$pres,
$build = converse.env.$build,
_ = converse.env._,
dayjs = converse.env.dayjs;
const { Backbone, Promise, Strophe, dayjs, sizzle, _, $build, $iq, $msg, $pres } = converse.env;
These dependencies are closured so that they don't pollute the global
namespace, that's why you need to access them in such a way inside the module.
......@@ -203,22 +159,117 @@ namespace, that's why you need to access them in such a way inside the module.
Overrides
---------
Plugins can override core code or code from other plugins. Refer to the full
example at the bottom for code details.
Plugins can override core code or code from other plugins. You can specify
overrides in the object passed to ``converse.plugins.add``.
In an override you can still call the overridden function, by calling
``this.__super__.methodName.apply(this, arguments);`` where ``methodName`` is
the name of the function or method you're overriding.
The following code snippet provides an example of two different overrides:
.. code-block:: javascript
overrides: {
/* The *_converse* object has a method "onConnected".
* You can override that method as follows:
*/
onConnected: function () {
// Overrides the onConnected method in Converse
// Top-level functions in "overrides" are bound to the
// inner "_converse" object.
const _converse = this;
Use the ``overrides`` functionality with caution. It basically resorts to
// Your custom code can come here ...
// You can access the original function being overridden
// via the __super__ attribute.
// Make sure to pass on the arguments supplied to this
// function and also to apply the proper "this" object.
_converse.__super__.onConnected.apply(this, arguments);
// Your custom code can come here ...
},
/* On the XMPPStatus Backbone model is a method sendPresence.
* We can override is as follows:
*/
XMPPStatus: {
sendPresence: function (type, status_message, jid) {
// The "_converse" object is available via the __super__
// attribute.
const _converse = this.__super__._converse;
// Custom code can come here ...
// You can call the original overridden method, by
// accessing it via the __super__ attribute.
// When calling it, you need to apply the proper
// context as reference by the "this" variable.
this.__super__.sendPresence.apply(this, arguments);
}
}
}
Use the ``overrides`` feature with caution. It basically resorts to
monkey patching which pollutes the call stack and can make your code fragile
and prone to bugs when Converse.js gets updated. Too much use of ``overrides``
and prone to bugs when Converse gets updated. Too much use of ``overrides``
is therefore a "code smell" which should ideally be avoided.
A better approach is to listen to the events emitted by Converse.js, and to add
A better approach is to listen to the events emitted by Converse, and to add
your code in event handlers. This is however not always possible, in which case
the overrides are a powerful tool.
Also, while it's possible to add new methods to classes via the ``overrides``
feature, it's better and more explicit to use composition with
``Object.assign``.
For example:
.. code-block:: javascript
function doSomething () {
// Your code comes here
}
Object.assign(_converse.ChatBoxView.prototype, { doSomething });
Overriding a template
~~~~~~~~~~~~~~~~~~~~~
Converse uses various templates, loaded with lodash, to generate its HTML.
It's not possible to override a template with the plugin's ``overrides``
feature, instead you should configure a new path to your own template via your
module bundler.
For example, with Webpack (which Converse uses internall), you can specify an
``alias`` for the template you want to override. This alias then points to your
own custom template.
For example, in your webpack config file, you could add the following to the
``config`` object that gets exported:
.. code-block:: javascript
resolve: {
extensions: ['.js'],
modules: [
path.join(__dirname, 'node_modules'),
path.join(__dirname, 'node_modules/converse.js/src')
],
alias: {
'templates/profile_view.html$': path.resolve(__dirname, 'templates/profile_view.html')
}
}
.. _`dependencies`:
Plugin dependencies
~~~~~~~~~~~~~~~~~~~
-------------------
When using ``overrides``, the code that you want to override (which is either
in ``converse-core`` or in other plugins), needs to be parsed already by the
......@@ -246,10 +297,10 @@ In this case, you can't specify the plugin as a dependency in the ``define``
statement at the top of the plugin, since it might not always be available,
which would cause ``require.js`` to throw an error.
Extending converse.js's configuration settings
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Extending Converse's configuration settings
----------------------------------------------
Converse.js comes with various :ref:`configuration-settings` that can be used to
Converse comes with various :ref:`configuration-settings` that can be used to
modify its functionality and behavior.
All configuration settings have default values which can be overridden when
......@@ -260,9 +311,9 @@ these settings with the `_converse.api.settings.update` method (see
:ref:`settings-update`).
Exposing promises
~~~~~~~~~~~~~~~~~
-----------------
Converse.js has a ``waitUntil`` API method (see :ref:`waituntil-grouping`)
Converse has a ``waitUntil`` API method (see :ref:`waituntil-grouping`)
which allows you to wait for various promises to resolve before executing a
piece of code.
......@@ -276,7 +327,7 @@ only resolves the plugin but will also emit an event with the same name.
Dealing with asynchronicity
---------------------------
Due to the asynchronous nature of XMPP, many subroutines in Converse.js execute
Due to the asynchronous nature of XMPP, many subroutines in Converse execute
at different times and not necessarily in the same order.
In many cases, when you want to execute a piece of code in a plugin, you first
......@@ -366,13 +417,7 @@ generated by `generator-conversejs <https://github.com/jcbrand/generator-convers
// Commonly used utilities and variables can be found under the "env"
// namespace of the "converse" global.
const Strophe = converse.env.Strophe,
$iq = converse.env.$iq,
$msg = converse.env.$msg,
$pres = converse.env.$pres,
$build = converse.env.$build,
_ = converse.env._,
dayjs = converse.env.dayjs;
const { Backbone, Promise, Strophe, dayjs, sizzle, _, $build, $iq, $msg, $pres } = converse.env;
// The following line registers your plugin.
converse.plugins.add("myplugin", {
......@@ -390,13 +435,13 @@ generated by `generator-conversejs <https://github.com/jcbrand/generator-convers
* If the setting "strict_plugin_dependencies" is set to true,
* an error will be raised if the plugin is not found.
*/
'dependencies': [],
dependencies: [],
/* Converse.js's plugin mechanism will call the initialize
/* Converse's plugin mechanism will call the initialize
* method on any plugin (if it exists) as soon as the plugin has
* been loaded.
*/
'initialize': function () {
initialize: function () {
/* Inside this method, you have access to the private
* `_converse` object.
*/
......@@ -453,15 +498,15 @@ generated by `generator-conversejs <https://github.com/jcbrand/generator-convers
},
/* If you want to override some function or a Backbone model or
* view defined elsewhere in converse.js, then you do that under
* view defined elsewhere in Converse, then you do that under
* the "overrides" namespace.
*/
'overrides': {
overrides: {
/* For example, the private *_converse* object has a
* method "onConnected". You can override that method as follows:
*/
'onConnected': function () {
// Overrides the onConnected method in converse.js
onConnected: function () {
// Overrides the onConnected method in Converse
// Top-level functions in "overrides" are bound to the
// inner "_converse" object.
......@@ -478,11 +523,11 @@ generated by `generator-conversejs <https://github.com/jcbrand/generator-convers
// Your custom code can come here ...
},
/* Override converse.js's XMPPStatus Backbone model so that we can override the
/* Override Converse's XMPPStatus Backbone model so that we can override the
* function that sends out the presence stanza.
*/
'XMPPStatus': {
'sendPresence': function (type, status_message, jid) {
XMPPStatus: {
sendPresence: function (type, status_message, jid) {
// The "_converse" object is available via the __super__
// attribute.
const _converse = this.__super__._converse;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment