Aloha Editor

These guides help you to make your content editable and to develop Aloha Editor.

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 – Stylesheets
    • doc – Documentation, if exists
    • img – Image assets
    • lib – RequireJS modules
    • nls – Localization files, see “Localization and I18N”: #Localization
    • res – other resources which do not fit into other directories, like JavaScript templates
    • test – Unit tests
    • vendor – Legacy JavaScript dependencies, not managed by RequireJS
    • package.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.

/custom/helloworld/lib/helloworld-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.

pluginA/lib/pluginA-plugin.js

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:

myplugin/nls/i18n.js

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:

myplugin/nls/de/i18n.js

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:

myplugin/lib/something.js

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:

myplugin/lib/myplugin-plugin.js

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:

pluginA/lib/pluginA-plugin.js

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