Launch – Make an Extension – Techniques & Types

We’re getting closer to a dozen articles on the topic of Launch Extensions! In my book, that is great, because I want everyone of you to a) be able to and b) do it!

Let me preface this one with a warning: I may have called myself a developer very briefly back in the late 90s, but since then, the world has moved on significantly, and I probably wasn’t that great to begin with.

Nevertheless, I would like to try and show how you can write Data Element Types, Action Types, Condition Types, and even Event Types, today. (That “today” refers to “show you”, not you writing them, although I wouldn’t object if you did.)

If you are a developer, and you should be one, otherwise why would you read this?! So, if you are a developer, and you can improve on my examples, please do!

CommonJS 101

As far as I understand it,. CommonJS Modules is a format that is heavily used in anything around NodeJS. All Node modules adhere to the CommonJS Modules standard.

In the context of Launch, all Javascript code you put into your Extension should be a CommonJS module.

To simplify it down radically, your files should always export “something”.

Launch will require your code, i.e. load it and then execute it when the time comes.

You can also use modules in your code, such as the main module, or any code that you want to encapsulate. Shared Modules also come along as modules, exported from other Extensions.

When you run the scaffolding tool and have it generate any Javascript file, the format will always look like this:

'use strict';

module.exports = function(settings) {
    // TODO
}

I’d like that to be slightly more verbose, something like this:

'use strict';

// any code here will be executed on module initialisation

module.exports = function(settings) {
    // any code here will be executed when Launch calls this module
}

So that’s the framework we are working with. You define a function, Launch calls it, magic happens.

Data Element Types

The simplest Data Element Type you could ever build would be one that always returns the same value, no configuration or anything:

'use strict';

module.exports = function(settings) {
    return true;
}

When any code in Launch needs the value of the Data Element, or when you call _satellite.getVar(), Launch will call the exported function, and the way we defined it, that function will simply return true, every time.

In reality, you’d have a screen on which users of Launch could configure the Data Element, and you’d like have some more elaborated Javascript in your function.

The settings parameter contains whatever the user configured, in whatever way your Data Element Configuration View stored it.

Say your Data Element Type returns some number, and there was an “exaggerate” flag that the user could set, you might end up with this:

'use strict';

module.exports = function(settings) {
    var someNumber;
    // TODO get the number from somewhere
    if (settings.exaggerate) {
        someNumber = 1000 * someNumber;
    }
    return someNumber;
}

Makes sense?

Data Element Types are pretty easy, I guess.

Condition Types

Condition Types are very similar to Data Element Types, except they should return either true or false.

They can also have settings, plus they can do limited introspection: they can check which Rule they are part of, or which Event triggered that Rule.

'use strict';

module.exports = function(settings, event) {
    // TODO
}

Let me go out on a limb here and say that I believe using the data in the event parameter inside a Condition Type is not good practice. But it can be useful for debugging and logging.

You can see the contents of the event parameter in the online help, under Contextual Event Data.

So, Condition Types, also pretty easy.

Action Types

Action Types are different. They are what makes Launch do stuff. Actions! Excitement! Can do! Yes!

The format of an Action Type module is exactly the same as before, but we don’t need a return statement, really.

In a sense, Action Types are even simpler than the others: Launch will call the exported function, and within the function, you do whatever has to be done.

Everyone’s favourite example is to pop an alert into visitors’ faces, which is as easy as this:

'use strict';

module.exports = function(settings) {
    alert(settings.message);
}

I think it is safe to say that you really shouldn’t do this.

Again, Action Types can access Contextual Event Data, if needed. Great for debugging, probably not good for actually actioning things.

Event Types

So far, Launch was in the driver seat, as in: “Launch will call …”

For Event Types, this is different. Event Types are part of Rules, but it really makes a lot more sense to think of them as single entities, a bit like tigers, roaming, alone, while the Rules watch their every step.

Launch will still call your exported function, but that will not trigger the Event. Instead, it will pass a trigger parameter into your function, and it is your job to manage that. The call is much more of a setup thing.

So, really, the tigers have this service where a Rule can say “let me know when you find what you’re looking for, ok?” and this is where I should drop the analogy.

What that means is that your Event Types need to be able to manage multiple listeners, something I badly neglected when I started building Extensions.

There is sample code in the documentation on Respecting Rule Order, which shows that the code should look like this:

'use strict';

var triggers = [];

// TODO code that listens or otherwise determines when your event triggers
// within that code:
    triggers.forEach(function(trigger) {
        trigger();
    });
// 

module.exports = function(settings, trigger) {
    // TODO modify things if needed 
    triggers.push(trigger);
};

Does that make any sense at this point? No?

The bulk of your responsibility when you write an Event Type is the code that sets up your Event. The code that Launch will call at initialisation. That’s where the magic is.

Want to see examples? Sure.

An Event Type that triggers as soon as Launch has initialised:

'use strict';

module.exports = function(settings, trigger) {
    trigger();
};

See how I did not queue the trigger here? I am pretty sure that is not needed, because all this Event Type does is trigger immediately.

How about an Event Type that triggers after a user-defined number of seconds?

Here you go:

'use strict';

module.exports = function(settings, trigger) {
    setTimeout(function() {
        trigger();
    }, settings.numberOfSeconds);
};

Again, no queuing, no need.

Let’s take the example from the documentation:

'use strict';

var triggers = [];

window.addEventListener('orientationchange', function() {
    triggers.forEach(function(trigger) {
        trigger();
    });
});

module.exports = function(settings, trigger) {
    triggers.push(trigger);
};

This, I guess, is the “Orientation Changed” Event Type, provided by the “Core” Extension.

It installs a handler for the “orientationchange” Javascript event, which calls all triggers in the right order whenever the visitor turns the screen.

Do something like this if your Event Type is driven by visitor behaviour.

Shared Modules

If you think your Extension should provide functionality for other Extensions to use, create a Shared Module.

The format is the same as for the others:

'use strict';

module.exports = // whatever it is your Shared Module exports

The exports can be a function, or a value, or really anything.

Other Extensions can use it like so:

var thatSharedModule = turbine.getSharedModule("your-extension-id", "your-shared-module-id");

If your Shared Module returns a value, thatSharedModule will now contain the value. If your Shared Module returns a function, thatSharedModule can now be called like any other function.

Simple.

Notes

I hope that even Event Types do make sense now. Overall, writing an Extension is surprisingly easy and straight-forward. Even I can do it!

There are some more complex concepts at work here, but they are not needed in order to understand the basics.

As an example, think of a Condition Type and a Data Element Type that use “remote data”, as in data that has to be queried somewhere else.

It will probably make sense to work with Promises here, and your Condition or Data Element Type should probably cache the result.

All of that is internal logic, only relevant to your implementation, not the structure.

One thought on “Launch – Make an Extension – Techniques & Types

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.