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 RequireJS modules explained
We use RequireJS to manage JavaScript dependencies, that’s why some basic understanding is very helpful. By using RequireJS, we gain some nice benefits: First, we the dependency management between JavaScript files is done for us, meaning they are included in the correct order.
The basic element of RequireJS is a so called module which is one file that defines and exports a JavaScript object. This reference can be used in other modules by requiring that module.
A module does not need to define its own name, it is simply named by its location on the file system.
Modules are created using define(dependencies, callback), where the first parameter dependencies
is an array of other RequireJS modules this plugin needs in order to function properly. The second parameter is callback
is a function which is executed as soon as all dependencies are loaded. The first dependency is passed to the callback
as first parameter, the second dependency as the second, and so on.
Circular dependencies need some special treatment. It is most often a sign of bad code, but if you need to do this, follow the guide at the RequireJS website.
You may use modules by requireing them if you specified the dependency you may use:
var module = require( 'module' );
If you want to load a module dynamically without defining a dependency you may use:
// note the array! require( ['module'], function( module ) { // module available here });
2 Using require
You may user require to define modules as well as requiring dynamically other modules.
To define a module you need to define it with its dependencies and a callback function. The callback function gets the defined modules as parameters in order you defined them.
define( [ 'dependendmodule1', 'dependendmodule2' ], function( dependendmodule1, dependendmodule2 ) { "use strict"; // you can use dependendmodule1, dependendmodule2 here. // both are loaded. return {}; });
You do not need to define all parameter of the callback function but require the module as needed. In order to not interfere with other modules we require all Aloha Editor modules in the aloha context. The configuration is available in Aloha.settings.requireConf. The method Aloha.require() is a shorthand to require with the Aloha Editor configuration and additionally gives you the Aloha object an argument if you pass only a callback function or as return if you pass no argument at all.
define( [ 'dependendmodule1', 'dependendmodule2' ], function() { "use strict"; var Aloha = Aloha.require(), dependendmodule1 = require( Aloha.settings.requireConf, 'dependendmodule1' ), dependendmodule2 = Aloha.require( 'dependendmodule2' ); // shorthand to the above Aloha.require( function( localAloha ) { // localAloha is available here }; return {}; });
You may also require module or Aloha itself on demand
( function() { "use strict"; Aloha.require( function( Aloha ) { // localAloha is available here // but not neccessarily fully initialized Aloha.jQuery('body').bind('aloha', function() { // Aloha is initialized here }); }); Aloha.require( ['dependendmodule1'], function( dependendmodule1 ) { // dependendmodule1 is available here }; return {}; });
3 Aloha plugins and their dependencies
In the following example, we define a components plugin with two modules. The first module componenttype
defines two classes. The second module component
has a dependency to componenttype
, and RequireJS injects what has been returned by componenttype
.
The plugin requires the module aloha/plugin which gives you the plugin factory method create()
define( [ 'aloha/plugin' ], function( plugin ) { "use strict"; // we do nothing but provide component modules... return plugin.create( 'plugin1', {}); });
The componenttye has no dependency thus the dependency array is empty.
define( [], // no dependency function() { "use strict"; var abstractModule = Class.extend({ doOther: function() { return 'didOther'; } }); return abstractModule; });
The component depends on the local module componenttype and defines its dependency.
define( ['./abstractmodule'], // dependency in the same path function( abstractModule ) { "use strict"; var module = abstractModule.extend({ doSome: function() { return 'didSome'; } }); return new module(); });
3.1 Cross plugin dependencies
If we want to use cross plugin modules you should not define a dependency on plugin1/module from plugin1 because that would hinder your plugin from working, because of the unfullfilled dependency. Instead require the module in your code with a Aloha.require() and extend the module with our own function when plugin2 is loaded. If another plugin would require plugin1/module it would have our extended functionality.
define( [ 'aloha/plugin' ], function( plugin, module ) { "use strict"; // now we require the the modue of plugin1 Aloha.require( [ 'plugin1/module' ], function( module ) } // module is available here module.doThis = function() { return 'didThis'; }; }); // we do nothing but provide component modules... return plugin.create( 'plugin2', {}); });
4 Aloha require path mapping for plugins
So, at which place does Aloha look up modules? We’ll explain this with a few examples:
Definition in RequireJS | File Name |
---|---|
myplugin/foo |
path_to_bundle/myplugin/lib/foo.js Including other RequireJS modules is the most common case |
myplugin/vendor/legacy_lib |
path_to_bundle/myplugin/vendor/legacy_lib.js Include a legacy library |
css!myplugin/css/cssfile.css |
path_to_bundle/myplugin/css/cssfile.css Includes the CSS file in the document <head> . |
i18n!myplugin/nls/i18n.js |
uses Aloha.settings.locale or tries to detect the language from the browser. |
text!myplugin/res/template.html |
Load the content of a text file (e.g. templates). |
The bundle name is not part of the RequireJS definition, as this is installation-specific. You as plugin developer should not care about where other plugins are located on disk.
5 Changelog
- July 3, 2011: Initial version by Sebastian Kurfürst and Christopher Hlubek