Adapter development

First steps

Find a smart name

  1. Avoid version numbers, except when there is major changes in the device API compared to a previous adapter
  2. It will be used in a lot of file names or as string id in the JS and CSS code. Dont take any risks : no space, not other character than [a-z0-9]
    Eg: android, samsungtv, node

Make sure you understand Joshfire's script loading and bootstrap system

If not, go back to Script loading

Declare your new adapter

Create a directory with a first file : src/adapters/DEVICE/global.js

This file is executed in the bootstrap of the framework, before RequireJS is even included. You can use it to declare global variables, and provide device-specific implementations of methods under the Joshfire namespace.

Here is a sample global.js file. Feel free to check the ones of existing adapters in the source.


(function(J) {
  
  J.onReady = function(callback) {
    Device_Specific_onDomReady(callback);
  };
  
  New_Global_Variable = Device_Specific_Init();
  
})(Joshfire);

Don't forget to re-run fab bootstraps after editing global.js or adding new modules to your connector.

Adapting Joshfire base UI elements

Create a new file with the same path as the base implementation but in the adapter directory. Eg: to adapt the video element, the base file is src/uielements/video.js, so you need to create src/adapters/DEVICE/lib/uielements/video.js

Usually the adapter implementation will extend the base class like this:


Joshfire.define([ 'joshfire/class', '../../../uielements/video.js', function(Class, Video) {

  return Class(Video, {
    
    // Provide a custom play() function
    play: function(options) {
      Device_Specific_Play(options.url);
    }
    
  });
});

Note the '../../../uielements/video.js' used to lookup the base file located in src/uielements/video.js. This is needed because just writing 'joshfire/uielements/video.js' would have actually loaded the file you're writing inside the adapter! (Check Script loading to understand why).

Looking at the source of the UI Elements implementations in the src/adapters/DEVICE/uielements/ directories is a great way to learn.

The UI Element API reference should also be useful for knowing which properties and methods to implement.

Reusing code from other adapters

It is common for adapters to share code. There are two ways to do it:

Manually

Do this when you just need to borrow a few modules from other adapters.

Suppose you're writing a new video UI Element in the Android connector based on the video UI Element in the Browser connector. You would write this:


Joshfire.define(['joshfire/class', 'joshfire/adapters/browser/uielements/video.js'], function(Class, Video) {
  
  return Class(Video, {
    
    // Just change a few details
    setup:function(callback) {
      this.Android_Specific_Variable = true;
      
      // Call setup() function in base class
      this.__super(callback);
    }
    
  });   
});

Declaring a global dependency

When an adapter is generally extending another you can declare a dependency between both of them.

For instance, the GoogleTV adapter has only code to support a few key codes from the remote but is similar in every other way to the Browser adapter. This is declared in the src/adapters/googletv/global.js file:


(function(J) {

  J.adapterDeps = ['browser'];
  
})(Joshfire);

The lookup sequence when loading joshfire/uielements/video will now be:

Adapting device inputs

Your device might be a touchscreen, have a remote control, a mouse, a keyboard or something entirely different like a camera that interprets body gestures. For this reason input devices don't interact directly with the UI Elements, instead they send generic input events to the application like right, left, enter and so on.

This event-driven architecture allows even remote devices to control your apps via HTTP packets or websockets for instance.

It is up to you to translate user actions to input events. Create src/adapters/DEVICE/inputs/INPUT.js and either:

Here is an example of a networked input you could implement:


Joshfire.define(['joshfire/input', 'joshfire/class'], function(Input, Class) {
  
  return Class(Input, {
    
    setup: function(callbackOnReady) {
      
      // this device listens to network events, so the onReady callback function
      // is called only once the network is initialized
      DeviceNetwork.onConnect = function() {
        callbackOnReady(null);
        
        // Wait for messages and forward them to the app as input events
        DeviceNetwork.onMessage = function(message) {
          if (message === 'MOVE_LEFT')
            self.app.publish('input', ['left']);
        };
      };
    }
  });
});

New inputs will need to be reference in the app.options.inputs array (see below).

Adapting the App class

You may overwrite any aspects of the initialization or inner workings of the base App class in your src/adapters/DEVICE/app.js file.

If you have developed new input methods, you could add them to the default options so that they are automatically loaded whenever an app is bootstraped with your adapter. Here's how:


Joshfire.define(
  ['joshfire/class', '../../app', 'joshfire/inputs/my_http_input','joshfire/vendor/underscore'],
  function(Class, App, MyHTTPInput, _) {
    
    return Class(App, {
      
      // Build the default options of any instanciated app
      getDefaultOptions: function() {
        
        // Overwrite the 'inputs' field with your new input class
        return _.extend(this.__super(), {
            'inputs': [MyHTTPInput]
        });
      }
    });
});

CSS

In order to help application developers, you might need to provide generic fixes or CSS reset rules. You can put .css or .scss ( see http://sass-lang.com/) files in the /css/adapters/DEVICE/ directory.

Packaging

Some environments (like Samsung TV) cannot run in Development mode and need to load only one JavaScript file like in Production mode. To help developers to do so, you may provide build scripts.

You may also bundle a default project template so that developer can bootstrap new projects with just one command.

All these features should be located in the build/adapter/DEVICE/ directory.