Pārlūkot izejas kodu

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
JC Brand 6 gadi atpakaļ
vecāks
revīzija
e4f04f7bdf
1 mainītis faili ar 133 papildinājumiem un 88 dzēšanām
  1. 133 88
      docs/source/plugin_development.rst

+ 133 - 88
docs/source/plugin_development.rst

@@ -10,30 +10,30 @@ Writing a plugin
 Introduction
 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.
 and extend its functionality.
 
 
 Using plugins is good engineering practice, and using them is the *only* recommended
 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:
 In particular, plugins have the following advantages:
 
 
 The main benefit of plugins is their *isolation of concerns* (and features).
 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
 From this benefit flows various 2nd degree advantages, such as the ability to
 make smaller production builds (by excluding unused plugins) and an easier
 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
 called `pluggable.js <https://github.com/jcbrand/pluggable.js/>`_, provides you
 with the ability to "hook in" to the core code and other plugins.
 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
 `src <https://github.com/jcbrand/converse.js/tree/master/src>`_ directory. All
 the files that follow the pattern `converse-*.js` are plugins.
 the files that follow the pattern `converse-*.js` are plugins.
 
 
 Plugins (by way of Pluggable.js) enable developers to extend and override existing objects,
 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
 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,
 Besides that, in plugins you can also write new Backbone (or other) models and views,
 in order to add a new functionality.
 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**
 .. 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.
     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/
     initialized and also when a chat message gets rendered: https://jsfiddle.net/4drfaok0/15/
 
 
 
 
 .. note:: **Generating a plugin with Yeoman**
 .. 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.
     ends with a documented example of a plugin.
 
 
     There is a `Yeoman <http://yeoman.io/>`_ code generator, called
     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
 Plugins need to be registered (and whitelisted) before they can be loaded and
 initialized.
 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
 The plugin itself is a JavaScript object which usually has at least an
 ``initialize`` method, which gets called at the end of the
 ``initialize`` method, which gets called at the end of the
 ``converse.initialize`` method which is the top-level method that gets called
 ``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:
 Here's an example code snippet:
 
 
@@ -101,7 +101,7 @@ Here's an example code snippet:
 Whitelisting of plugins
 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
 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.
 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``.
 you call ``converse.initialize``.
 
 
 If you're adding a "core" plugin, which means a plugin that will be
 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
 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>`_.
 `./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>`_.
 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
 encapsulate sensitive information and methods which should not be exposed
 to any 3rd-party scripts that might be running in the same page.
 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
 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
 Immediately inside the module shown above you can access 3rd party libraries (such
 dayjs and lodash) via the ``converse.env`` map.
 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
 .. code-block:: javascript
 
 
     // Commonly used utilities and variables can be found under the "env"
     // Commonly used utilities and variables can be found under the "env"
     // namespace of the "converse" global.
     // 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
 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.
 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
 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
 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.
 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
 your code in event handlers. This is however not always possible, in which case
 the overrides are a powerful tool.
 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`:
 .. _`dependencies`:
 
 
 Plugin dependencies
 Plugin dependencies
-~~~~~~~~~~~~~~~~~~~
+-------------------
 
 
 When using ``overrides``, the code that you want to override (which is either
 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
 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,
 statement at the top of the plugin, since it might not always be available,
 which would cause ``require.js`` to throw an error.
 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.
 modify its functionality and behavior.
 
 
 All configuration settings have default values which can be overridden when
 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`).
 :ref:`settings-update`).
 
 
 Exposing promises
 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
 which allows you to wait for various promises to resolve before executing a
 piece of code.
 piece of code.
 
 
@@ -276,7 +327,7 @@ only resolves the plugin but will also emit an event with the same name.
 Dealing with asynchronicity
 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.
 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
 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"
     // Commonly used utilities and variables can be found under the "env"
     // namespace of the "converse" global.
     // 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.
     // The following line registers your plugin.
     converse.plugins.add("myplugin", {
     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,
          * If the setting "strict_plugin_dependencies" is set to true,
          * an error will be raised if the plugin is not found.
          * 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
          * method on any plugin (if it exists) as soon as the plugin has
          * been loaded.
          * been loaded.
          */
          */
-        'initialize': function () {
+        initialize: function () {
             /* Inside this method, you have access to the private
             /* Inside this method, you have access to the private
              * `_converse` object.
              * `_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
         /* 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.
          * the "overrides" namespace.
          */
          */
-        'overrides': {
+        overrides: {
             /* For example, the private *_converse* object has a
             /* For example, the private *_converse* object has a
              * method "onConnected". You can override that method as follows:
              * 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
                 // Top-level functions in "overrides" are bound to the
                 // inner "_converse" object.
                 // inner "_converse" object.
@@ -478,11 +523,11 @@ generated by `generator-conversejs <https://github.com/jcbrand/generator-convers
                 // Your custom code can come here ...
                 // 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.
              * 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__
                     // The "_converse" object is available via the __super__
                     // attribute.
                     // attribute.
                     const _converse = this.__super__._converse;
                     const _converse = this.__super__._converse;