Writing Plugins
After reading this guide, you will be able to:
- Create plugins to adjust Aloha Editor to your needs.
- Load and define dependencies with RequireJS.
- Include other resources from your plugins
1 What’s a plugin?
A plugin extends Aloha Editor with new behavior and can be loaded by the user asynchronously. It should be self-contained, so that it can be used for other Aloha installations.
A full-blown plugin has the following file structure:
pluginname
css
– Stylesheetsdoc
– Documentation, if existsimg
– Image assetslib
– RequireJS modulesnls
– Localization files, see “Localization and I18N”: #Localizationres
– other resources which do not fit into other directories, like JavaScript templatestest
– Unit testsvendor
– Legacy JavaScript dependencies, not managed by RequireJSpackage.json
– Metadata about your package, in CommonJS format
All file names are, by convention, lowercase.
2 A basic Aloha plugin
For a very basic plugin, nothing else than a plugin module in lib/
has to be created. The module is named as pluginname-plugin.js.
define( [ 'aloha/plugin' ], function( plugin ) { "use strict"; return plugin.create( 'helloworld', { init: function() { // Executed on plugin initialization } }); });
If your plugin should be published to the public you should consider writing a package.json
file with metadata about the plugin. Furthermore, the name of your plugin name should be globally unique.
This code requires the core plugin module (which defines the plugin
class) and creates a plugin with the name pluginA
.
define( [ 'aloha/plugin', 'aloha/console'], function( plugin, console ) { "use strict"; return plugin.create( 'pluginA', { defaults: { value: 10 }, init: function() { // Executed on plugin initialization console.log( this.settings.value ); } }); });
As developer you can define plugin default settings by specifying Aloha.defaults.pluginA and these default setting will be merged with the Aloha.setttings. Once defined as default you can always access the settings without extensive checking its existence.
To read the settings of a specific plugin you can use
var settings = Aloha.require('pluginA/pluginA-plugin').settings;
3 Localization
If you have some labels in your plugin, these need to be made available in multiple languages, as Aloha is capable showing its user interface in a wide variety of languages.
Localization is done using a slightly adjusted version of the i18n
module of RequireJS
3.1 Folder Structure for Labels
Localized strings are stored in the folder nls
(Natural Language Support) of your plugin. The file/directory structure inside nls
is as follows:
nls/
i18n.js
– contains the English strings, used as fallback.de/
i18n.js
– contains the German strings
fr/
i18n.js
– contains the French strings
- … continue for the other languages …
The English i18n file has the following structure:
define({ "root": { "your.key.here": "Label in English", "your.other.key.here": "Some more Text", // continue here for all labels }, "de": true, "fr": true });
Inside root
, all the keys and labels are placed. Furthermore, when a German language file is available, the key de
needs to be set to true
, and this is done for all languages available.
A localized language file has a simpler structure:
define({ "your.key.here": "Beschreibung in Deutsch", "your.other.key.here": "Noch mehr Text", // continue here for all labels });
3.2 Using localized strings
Now, if we want to use the “your.key.here” key inside our application, we can use RequireJS to inject the correct language version for us:
define( ['i18n!myplugin/nls/i18n'], function(i18n) { // your code... var localizedString = i18n.t('your.key.here'); // your code... });
By defining i18n!myplugin/nls/i18n as requirement, we get a special object injected which contains all localized strings. There exists a t('key') method on this object which returns the translation for the passed key.
4 Loading resources
5 Including CSS files
When your Aloha plugin needs some CSS files, you should place them in the css
folder of your plugin. Your plugin can load the CSS file as follows:
define( ['core/plugin', 'css!myplugin/css/myplugin.css'], function(Plugin) { "use strict"; // Add this stage css is loaded return Plugin.create('myplugin', { init: function() { // Executed on plugin initialization } }); });
The key part is to set a dependency on css!myplugin/css/myplugin.css. Then, the loader knows that it needs to load the myplugin.css
stylesheet into the current website.
The css! requirejs plugin is just a convenience if you only need to load one or two additional plugins that are not included in the Aloha distribution Aloha. If you have many css files, they should be concatenated and minified to avoid performing many independent HTTP requests during loading, which significantly impacts load performance. When writing an Aloha common plugin, the css file must added to the main aloha css include src/css/aloha.css
.
By default, CSS file loading is asynchronous, meaning that CSS files are loaded in the background, independently from the JavaScript loading. If you for some reason depend on the fact that a CSS file is included at a particular point in time (calles synchronous loading), you need to append the parameter !nowait
to the dependency, so the whole dependency looks like css!myplugin/css/myplugin.css!nowait
6 Lifecycle of Plugins
Currently there are 2 different stages of Aloha plugins
- aloha-plugins-loaded means that all initially defined plugins and their dependencies are loaded.
- aloha-plugins-ready means that all plugins init method was called.
7 Dependencies between plugins
If a plugin A needs plugin B to work, you should not specify it as a dependency of plugin A in the define() methode, as this will break without a meaningful error message to the user.
Let’s see how to define a dependency to the block
-Plugin:
define( [ 'aloha/plugin' ], function( Plugin ) { "use strict"; return Plugin.create('pluginA', { dependencies: ['pluginB'], init: function() { // Executed on pluginA initialization // pluginB ist not available here Aloha.require( 'pluginA/module', function( module ) { // Use pluginB's module here }); } }); });
Here, we specify a dependency to another plugin using the dependencies property of our Plugin
. Then, the plugin manager makes sure that the dependent plugin is actually loaded, and outpus an error message if not. When everything is loaded, we can access a module from the other plugin by using the requireAloha() statement, as shown for the pluginB in the example above.
8 Changelog
- July 3, 2011: Initial version by Sebastian Kurfürst and Christopher Hlubek
- Sept 11, 2011: Adopted to current plugin lifecycle in sync with tests added resource loading by Haymo Meran