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 sub­classes, 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));