123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284 |
- .. raw:: html
- <div id="banner"><a href="https://github.com/jcbrand/converse.js/blob/master/docs/source/theming.rst">Edit me on GitHub</a></div>
- .. _`writing-a-plugin`:
- Writing a plugin
- ================
- .. contents:: Table of Contents
- :depth: 2
- :local:
- Introduction
- ------------
- Developers are able to extend and override the objects, functions and the
- Backbone models and views that make up converse.js by means of writing plugins.
- Converse.js uses `pluggable.js <https://github.com/jcbrand/pluggable.js/>`_ as
- its plugin architecture.
- To understand how this plugin architecture works, please read the
- `pluggable.js documentation <https://jcbrand.github.io/pluggable.js/>`_
- and to understand its inner workins, please refer to the `annotated source code
- <https://jcbrand.github.io/pluggable.js/docs/pluggable.html>`_.
- You register a converse.js plugin as follows:
- .. code-block:: javascript
- 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
- },
- });
- Security and access to the inner workings
- -----------------------------------------
- The globally available ``converse`` object, which exposes the API methods, such
- as ``initialize`` and ``plugins.add``, is a wrapper that encloses and protects
- a sensitive inner object, named ``_converse`` (not the underscore prefix).
- This inner ``_converse`` object contains all the Backbone models and views,
- as well as various other attributes and functions.
- Within a plugin, you will have access to this internal
- `"closured" <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Closures>`_
- ``_converse`` object, which is normally not exposed in the global variable scope.
- 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
- -----------------
- 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("myplugin", ["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
- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- Immediately inside the module shown above you can access 3rd party libraries (such
- moment, underscore and jQuery) via the ``converse.env`` map.
- The code for it would look something like this:
- .. code-block:: javascript
- // Commonly used utilities and variables can be found under the "env"
- // namespace of the "converse" global.
- var Strophe = converse.env.Strophe,
- $iq = converse.env.$iq,
- $msg = converse.env.$msg,
- $pres = converse.env.$pres,
- $build = converse.env.$build,
- b64_sha1 = converse.env.b64_sha1;
- $ = converse.env.jQuery,
- _ = converse.env._,
- moment = converse.env.moment;
- 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.
- Overrides
- ---------
- Plugins can override core code or code from other plugins. Refer to the full
- example at the bottom for code details.
- Use the ``overrides`` functionality 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``
- 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
- your code in event handlers. This is however not always possible, in which case
- the overrides are a powerful tool.
- Optional 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 loaded already by the
- type the ``overrides`` object is being parsed.
- So it's important to include overridden plugins in the AMD ``define`` statement
- at the top of the plugin module.
- However, sometimes you want to override parts of another plugin if it exists, but you
- don't want anything to break if it doesn't exist (for example when using a
- custom build which excludes that plugin). An example is the
- `converse-dragresize <https://github.com/jcbrand/converse.js/blob/master/src/converse-dragresize.js>`_
- plugin, which will add drag-resize handles to the headlines box (which shows
- messages of type ``headline``) but doesn't care if that particular plugin isn't
- actually loaded.
- 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.
- To resolve this problem we thave the ``optional_dependencies`` Array attribute.
- With this you can specify those dependencies which need to be loaded before
- your plugin, if they exist. If they don't exist, they won't be ignored.
- If the setting :ref:`strict_plugin_dependencies` is set to true,
- an error will be raised if the plugin is not found, thereby making them
- non-optional.
- A full example plugin
- ---------------------
- .. code-block:: javascript
- (function (root, factory) {
- if (typeof define === 'function' && define.amd) {
- // AMD. Register as a module called "myplugin"
- define("myplugin", ["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) {
- // Commonly used utilities and variables can be found under the "env"
- // namespace of the "converse" global.
- var Strophe = converse.env.Strophe,
- $iq = converse.env.$iq,
- $msg = converse.env.$msg,
- $pres = converse.env.$pres,
- $build = converse.env.$build,
- b64_sha1 = converse.env.b64_sha1;
- $ = converse.env.jQuery,
- _ = converse.env._,
- moment = converse.env.moment;
- // The following line registers your plugin.
- converse.plugins.add('myplugin', {
- initialize: function () {
- // Converse.js's plugin mechanism will call the initialize
- // method on any plugin (if it exists) as soon as the plugin has
- // been loaded.
- // Inside this method, you have access to the closured
- // _converse object, from which you can get any configuration
- // options that the user might have passed in via
- // converse.initialize. These values are stored in the
- // "user_settings" attribute.
- // Let's assume the user might pass in a custom setting, like so:
- //
- // converse.initialize({
- // "initialize_message": "My plugin has been initialized"
- // });
- //
- // Then we can alert that message, like so:
- alert(this._converse.user_settings.initialize_message);
- },
- // Optional dependencies are other plugins which might be
- // overridden or relied upon, and therefore need to be loaded before
- // this plugin. They are called "optional" because they might not be
- // available, in which case any overrides applicable to them will be
- // ignored.
- // It's possible however to make optional dependencies non-optional.
- // If the setting "strict_plugin_dependencies" is set to true,
- // an error will be raised if the plugin is not found.
- //
- // NB: These plugins need to have already been loaded via require.js.
- optional_dependencies: [],
- overrides: {
- // If you want to override some function or a Backbone model or
- // view defined elsewhere in converse.js, then you do that under
- // this "overrides" namespace.
- // For example, the inner protected *_converse* object has a
- // method "onConnected". You can override that method as follows:
- onConnected: function () {
- // Overrides the onConnected method in converse.js
- // Top-level functions in "overrides" are bound to the
- // inner "_converse" object.
- var _converse = this;
- // Your custom code comes 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);
- },
- XMPPStatus: {
- // Override converse.js's XMPPStatus Backbone model so that we can override the
- // function that sends out the presence stanza.
- sendPresence: function (type, status_message, jid) {
- // The "_converse" object is available via the __super__
- // attribute.
- var _converse = this.__super__._converse;
- // Custom code can come here
- // ...
- // You can call the original overridden method, by
- // accessing it via the __super__ attribute.
- // When calling it, you need to apply the proper
- // context as reference by the "this" variable.
- this.__super__.sendPresence.apply(this, arguments);
- }
- }
- }
- });
- }));
|