src/api.js
/*jslint indent:2,white:true,node:true,sloppy:true */
var PromiseCompat = require('es6-promise').Promise;
/**
* The API registry for freedom.js. Used to look up requested APIs,
* and provides a bridge for core APIs to act like normal APIs.
* @Class API
* @param {Debug} debug The debugger to use for logging.
* @constructor
*/
var Api = function(debug) {
this.debug = debug;
this.apis = {};
this.providers = {};
this.waiters = {};
};
/**
* Get an API.
* @method get
* @param {String} api The API name to get.
* @returns {{name:String, definition:API}} The API if registered.
*/
Api.prototype.get = function(api) {
if (!this.apis[api]) {
return false;
}
return {
name: api,
definition: this.apis[api]
};
};
/**
* Set an API to a definition.
* @method set
* @param {String} name The API name.
* @param {API} definition The JSON object defining the API.
*/
Api.prototype.set = function(name, definition) {
this.apis[name] = definition;
};
/**
* Register a core API provider.
* @method register
* @param {String} name the API name.
* @param {Function} constructor the function to create a provider for the API.
* @param {String?} style The style the provider is written in. Valid styles
* are documented in fdom.port.Provider.prototype.getInterface. Defaults to
* provideAsynchronous
* @param {Object?} flags Prefixed arguments needed by the core provider.
* valid keys are 'module', 'provider', and 'config'.
*/
Api.prototype.register = function(name, constructor, style, flags) {
var i,
args;
this.providers[name] = {
constructor: constructor,
style: style || 'provideAsynchronous',
flags: flags || {}
};
if (this.waiters[name]) {
for (i = 0; i < this.waiters[name].length; i += 1) {
args = {};
if (flags.module) {
args.module = this.waiters[name][i].from;
}
if (flags.config) {
args.config = this.waiters[name][i].from.config;
}
this.waiters[name][i].resolve({
args: args,
inst: constructor.bind({}, args)
});
}
delete this.waiters[name];
}
};
/**
* Get a core API connected to a given FreeDOM module.
* @method getCore
* @param {String} name the API to retrieve.
* @param {Module} from The instantiating App.
* @returns {Promise} A promise of a fdom.App look-alike (and argument object),
* matching a local API definition.
*/
Api.prototype.getCore = function(name, from) {
return new PromiseCompat(function(resolve, reject) {
if (this.apis[name]) {
if (this.providers[name]) {
var args = {};
if (this.providers[name].flags.module) {
args.module = from;
}
if (this.providers[name].flags.config) {
args.config = from.config;
}
resolve({
args: args,
inst: this.providers[name].constructor.bind({}, args)
});
} else {
if (!this.waiters[name]) {
this.waiters[name] = [];
}
this.waiters[name].push({
resolve: resolve,
reject: reject,
from: from
});
}
} else {
this.debug.warn('Api.getCore asked for unknown core: ' + name);
reject(null);
}
}.bind(this));
};
/**
* Configure a {Provider} to provide a named core api on behalf of a
* given port.
* @param {String} name The name of the provider
* @param {Provider} provider The provider that will provide the named api
* @param {Module} from The module requesting the core provider.
*/
Api.prototype.provideCore = function (name, provider, from) {
return this.getCore(name, from).then(function (core) {
var flags = this.providers[name].flags,
iface = provider.getProxyInterface();
if (flags.provider) {
core.args.provider = iface;
}
iface()[this.providers[name].style](core.inst);
}.bind(this), function (err) {
this.debug.error('Could not provide core: ', err);
}.bind(this));
};
/**
* Shutdown the API registry, and reject any pending waiters.
*/
Api.prototype.cleanup = function () {
var prop,
doReject = function (waiter) {
waiter.reject();
};
for (prop in this.waiters) {
if (this.waiters.hasOwnProperty(prop)) {
this.waiters[prop].forEach(doReject);
}
}
delete this.waiters;
};
/**
* Defines the apis module and provider registry.
*/
module.exports = Api;