Plugins
Spirits come with a set of plugins out of the box and there's a whole subsection of this site dedicated to the study of default plugins.
action
- Actions work much like jQuery triggers.
att
- Update attributes and observe attribute updates.
box
- Compute the position and dimension of a spirit.
broadcast
- Broadcast messages pub-sub style.
config
- Parsing special configuration attributes.
css
- maintain classnames and
style
properties. dom
- Find and manipulate elements.
event
- Add and remove event listeners.
life
- Tracking the lifecycle of a spirit.
tick
- Deal with scheduling and timing.
In Spiritual, a plugin is simply a group of related functionality that has been encapsulated under a single property name. Plugins offload complexity from the spirit and reduce the likelihood of naming collisions.
gui.Spirit.extend({
example: function() {
this.event.add('click');
this.action.add('pick');
this.broadcast.add('done');
}
});
Because a spirits methods are only available to the spirit and its subclasses, plugins are also useful for sharing functionality between spirits that would otherwise become stuck in class hierarchies.
Creating plugins
To create a plugin, you start by extending another plugin. The base plugin is gui.Plugin
.
var NumberPlugin = gui.Plugin.extend({
getRandom: function(max) {
return Math.round(Math.random() * max);
}
});
Then you just need to plug it into a spirit. The base spirit is gui.Spirit
.
gui.Spirit.plugin("number", NumberPlugin);
The plugin becomes available to gui.Spirit
and all spirits that inherit from it (in this case, all spirits). When the number
property is accessed, the plugin will be newed up.
var MySpirit = gui.Spirit.extend({
onconstruct: function() {
this.super.onconstruct();
console.log(this.number.getRandom());
}
});
Laziness
Plugins are instantiated lazily. If you want the plugin to do something without being accessed first, you can set the lazy
property to false
. This is a property on the plugin constructor
, so it's a static
property. There's a shorthand for declaring this. Read more about classes.
gui.Plugin.extend({
onconstruct: function() {
this.super.onconstruct();
console.log(this.getRandom());
},
getRandom: function() {
return Math.random();
}
}, {
lazy: false;
});
A non-lazy plugin like this instantiates when the onconfigure
method is called on the spirit. That's pretty much straight away. If the plugin needs to deal with the spirit (or any other plugin), it can access the spirit using this.spirit
.
gui.Plugin.extend({
onconstruct: function() {
this.super.onconstruct();
console.log(this.spirit); // [object gui.Spririt]
}
});
Lifecycle
The lifecycle of a plugin is bound the spirit. A lazy plugin will be newed up when the spirit starts using. A non-lazy plugin will be created when onconfigure
is called on the spirit. In both cases, the plugin will be destroyed when the
spirit is destroyed. You can hook into these things.
gui.Plugin.extend({
onconstruct: function() {
this.super.onconstruct();
console.log('Constructed.');
},
ondestruct: function() {
this.super.ondestruct();
console.log('Destructed.');
}
});
Once constructed, the plugin can also hook into the spirits lifecycle. The plugin might for example need to know when onenter
is called. We'll do this using the life
plugin. You can get a hold on the life
plugin through the spirit.
gui.Plugin.extend({
onconstruct: function() {
this.super.onconstruct();
this.spirit.life.add('enter', this);
},
onlife: function(l) {
console.log(l.type); // 'enter'
}
}, {
lazy: false;
});
For a full list of events, refer to the life
plugin. Note that unlike with spirits, you should rarely add this.super.onlife
in the callback method, since few plugins have this method predefined.
Method chaining
Plugins keep a tradition of method chaining as seen in jQuery. Unless a method returns something special, consider having it return this
to cut down on keystrokes.
gui.Spirit.extend({
example: function() {
this.action.
add('one').
remove('two').
dispatch('three');
}
});
There's an assistant function gui.Combo.chained
that makes a method return this
when it would otherwise return undefined
. It makes for a nice declarative syntax.
var MySpirit = (function using(chained) {
return gui.Spirit.extend({
dothis: chained(function() {}),
dothat: chained(function() {})
});
}(gui.Combo.chained));