Share Gaia assets and adapters, keep private profiles and development documents local, and update both public READMEs.
244 lines
7.6 KiB
JavaScript
Executable file
244 lines
7.6 KiB
JavaScript
Executable file
'use strict';
|
|
|
|
(function(exports) {
|
|
var DEBUG = false;
|
|
/**
|
|
* Shared some global property.
|
|
* @type {Object}
|
|
* @module Service
|
|
*/
|
|
exports.Service = {
|
|
/**
|
|
* Stores the servers by the server name.
|
|
* @type {Map}
|
|
*/
|
|
_providers: new Map(),
|
|
|
|
/**
|
|
* Stores the services by the services name.
|
|
* @type {Map}
|
|
*/
|
|
_services: new Map(),
|
|
|
|
/**
|
|
* Stores the awaiting consumers by the service name.
|
|
* @type {Map}
|
|
*/
|
|
_requestsByService: new Map(),
|
|
|
|
/**
|
|
* Stores the awaiting consumers by the server name.
|
|
* @type {Map}
|
|
*/
|
|
_requestsByProvider: new Map(),
|
|
|
|
/**
|
|
* Request a service and get a promise.
|
|
* The service name may include the name of server or not if it is unique.
|
|
*
|
|
* The request and query format are different,
|
|
* request does not accept getter format.
|
|
*
|
|
* @example
|
|
* Service.request('locked').then(function() {});
|
|
* Service.request('addObserver', 'test.enabled', this).then(function() {});
|
|
* Service.request('Statusbar:height').then(function() {});
|
|
*
|
|
* @param {String} service Service name
|
|
* @return {Promise}
|
|
*/
|
|
request: function(service) {
|
|
var requestItems = service.split(':');
|
|
var args = Array.prototype.slice.call(arguments, 1);
|
|
var self = this;
|
|
this.debug(requestItems);
|
|
if (requestItems.length > 1) {
|
|
var serverName = requestItems[0];
|
|
var serviceName = requestItems[1];
|
|
if (this._providers.get(serverName)) {
|
|
this.debug('service: ' + serviceName +
|
|
' is online, perform the request with ' + args.concat());
|
|
return Promise.resolve(this._providers.get(serverName)[serviceName]
|
|
.apply(self._providers.get(serverName), args));
|
|
} else {
|
|
return new Promise(function(resolve, reject) {
|
|
self.debug('service: ' + service + ' is offline, queue the task.');
|
|
if (!self._requestsByProvider.has(serverName)) {
|
|
self._requestsByProvider.set(serverName, []);
|
|
}
|
|
self._requestsByProvider.get(serverName).push({
|
|
service: serviceName,
|
|
resolve: resolve,
|
|
args: args
|
|
});
|
|
});
|
|
}
|
|
return;
|
|
}
|
|
if (this._services.has(service)) {
|
|
var server = this._services.get(service);
|
|
this.debug('service [' + service +
|
|
'] provider [' + server.name + '] is online, perform the task.');
|
|
return Promise.resolve(server[service].apply(server, args));
|
|
} else {
|
|
this.debug('service: ' + service + ' is offline, queue the task.');
|
|
var promise = new Promise(function(resolve, reject) {
|
|
self.debug('storing the requests...');
|
|
if (!self._requestsByService.has(service)) {
|
|
self._requestsByService.set(service, []);
|
|
}
|
|
self._requestsByService.get(service).push({
|
|
service: service,
|
|
resolve: resolve,
|
|
args: args
|
|
});
|
|
});
|
|
return promise;
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Register an asynchronous service.
|
|
* If there is any client awaiting this service, they will be executed after
|
|
* registration.
|
|
* @param {String} service Service name
|
|
* @param {Object} server The server object which has the service.
|
|
*/
|
|
register: function(service, server) {
|
|
var self = this;
|
|
if (!this._providers.has(server.name)) {
|
|
this._providers.set(server.name, server);
|
|
}
|
|
this.debug((server.name || '(Anonymous)') +
|
|
' is registering service: [' + service + ']');
|
|
|
|
this.debug('checking awaiting requests by server..');
|
|
if (this._requestsByProvider.has(server.name)) {
|
|
this._requestsByProvider.get(server.name).forEach(function(request) {
|
|
self.debug('resolving..', server,
|
|
server.name, request.service, request.args);
|
|
var returnValue = (typeof(server[request.service]) === 'function') ?
|
|
server[request.service].apply(server, request.args) :
|
|
server[request.service];
|
|
request.resolve(returnValue);
|
|
});
|
|
this._requestsByProvider.delete(server.name);
|
|
}
|
|
|
|
if (!this._services.has(service)) {
|
|
this._services.set(service, server);
|
|
} else {
|
|
this.debug('the service [' + service + '] has already been ' +
|
|
'registered by other server:', this._services.get(service).name);
|
|
return;
|
|
}
|
|
|
|
this.debug('checking awaiting requests by service..');
|
|
if (this._requestsByService.has(service)) {
|
|
this._requestsByService.get(service).forEach(function(request) {
|
|
self.debug('resolving..', server, request.service);
|
|
var returnValue = server[request.service].apply(server, request.args);
|
|
request.resolve(returnValue);
|
|
});
|
|
this._requestsByService.delete(service);
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Unregister an asynchronous service.
|
|
* @param {String} service The name of the service
|
|
* @param {Object} server The server
|
|
*/
|
|
unregister: function(service, server) {
|
|
this._providers.delete(server.name);
|
|
var se = this._services.get(service);
|
|
if (se && server === se) {
|
|
this._services.delete(service);
|
|
}
|
|
},
|
|
|
|
_states: new Map(),
|
|
_statesByState: new Map(),
|
|
|
|
registerState: function(state, provider) {
|
|
this._states.set(provider.name, provider);
|
|
if (!provider.name) {
|
|
console.warn('Service.registerState: Provider should have a name.',
|
|
provider);
|
|
}
|
|
this._statesByState.set(state, provider);
|
|
},
|
|
|
|
unregisterState: function(state, provider) {
|
|
this._states.delete(provider.name);
|
|
var machine = this._statesByState.get(state);
|
|
if (machine === provider) {
|
|
this._statesByState.delete(state);
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Synchonously query the state of specific state machine.
|
|
* If the state machine is not started,
|
|
* you will get undefined.
|
|
*
|
|
* @example
|
|
* Service.query('FtuLauncher.isFtuRunning');
|
|
* Service.query('isFtuRunning');
|
|
*
|
|
* @param {String} state The machine name and the state name.
|
|
* @return {String|Boolean|Number|Object}
|
|
*/
|
|
query: function(stateString) {
|
|
this.debug(stateString);
|
|
var args = stateString.split('.');
|
|
var state, provider;
|
|
if (args.length > 1) {
|
|
provider = this._states.get(args[0]);
|
|
state = args[1];
|
|
} else {
|
|
state = args[0];
|
|
provider = this._statesByState.get(state);
|
|
}
|
|
if (!provider) {
|
|
this.debug('Provider not ready, return undefined state.');
|
|
return undefined;
|
|
}
|
|
if (typeof(provider[state]) === 'function') {
|
|
var functionArgs = Array.prototype.slice.call(arguments, 1);
|
|
return provider[state].apply(provider, functionArgs);
|
|
} else {
|
|
return provider[state];
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Record the start time of the system for later debugging usage.
|
|
* @access private
|
|
* @type {Number}
|
|
* @memberOf module:Service
|
|
*/
|
|
_start: new Date().getTime() / 1000,
|
|
|
|
/**
|
|
* Get current time offset from the start.
|
|
* @return {Number} The time offset.
|
|
* @memberOf module:Service
|
|
*/
|
|
currentTime: function() {
|
|
return (new Date().getTime() / 1000 - this._start).toFixed(3);
|
|
},
|
|
|
|
debug: function sys_debug() {
|
|
if (DEBUG) {
|
|
console.log('[System]' +
|
|
'[' + window.Service.currentTime() + ']' +
|
|
Array.slice(arguments).concat());
|
|
}
|
|
},
|
|
|
|
get manifestURL() {
|
|
return window.location.href.replace('index.html', 'manifest.webapp');
|
|
}
|
|
};
|
|
})(window);
|