[a-z0-9]
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.
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.
It is common for adapters to share code. There are two ways to do it:
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);
}
});
});
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:
joshfire/adapters/googletv/uielements/video.js
: not foundjoshfire/adapters/browser/uielements/video.js
: found!joshfire/uielements/video.js
if the previous one hadn't been found)
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).
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]
});
}
});
});
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.
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.