VulpesOS/gaia/tv_apps/smart-system/js/service.js
OverSu b38dea0597 Publish Vulpes Preview 15 sources for desktop, Android and Tundra
Share Gaia assets and adapters, keep private profiles and development documents local, and update both public READMEs.
2026-09-30 23:13:03 +02:00

304 lines
8.9 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 to System and get a promise.
* The service name may include the name of server or not if it is unique.
* @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 new Promise(function(resolve, reject) {
resolve(self._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 new Promise(function(resolve, reject) {
resolve(server[service].apply(server, args));
});
} else {
this.debug('service: ' + service + ' is offline, queue the task.');
var promise = new Promise(function(resolve) {
self.debug('storing the requests...');
if (!self._requestsByService.has(service)) {
self._requestsByService.set(service, []);
}
self._requestsByService.get(service).push({
service: service,
args: args,
resolve: resolve
});
});
return promise;
}
},
/**
* Register an asynchronous service to 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 result = (typeof(server[request.service]) === 'function') ?
server[request.service].apply(server, request.args) :
server[request.service];
request.resolve(result);
});
this._requestsByProvider.delete(server.name);
}
if (!this._services.has(service)) {
this._services.set(service, server);
} else {
console.warn('the service [' + service + '] has already been ' +
'registered by other server.');
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);
request.resolve(server[request.service].apply(server, request.args));
});
this._requestsByService.delete(service);
}
},
/**
* Unregister an asynchronous service to System
* @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);
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}
*/
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') {
return provider[state]();
} else {
return provider[state];
}
},
/**
* XXX: applications should register a service
* for ready check by Service.register('ready', applications).
*/
get applicationReady() {
return window.applications && window.applications.ready;
},
/**
* Indicates the system is busy doing something.
* Now it stands for the foreground app is not loaded yet.
*
* XXX: AppWindowManager should register a service
* for isBusyLoading query by
* Service.register('isBusyLoading', appWindowManager).
*/
isBusyLoading: function() {
var app = window.AppWindowManager.getActiveApp();
return app && !app.loaded;
},
/**
* 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('[Service]' +
'[' + window.Service.currentTime() + ']' +
Array.slice(arguments).concat());
}
},
forceDebug: function sys_debug() {
console.log('[Service]' +
'[' + window.Service.currentTime() + ']' +
Array.slice(arguments).concat());
},
_dump: function sys__dump() {
try {
throw new Error('dump');
} catch (e) {
console.log(e.stack);
}
},
publish: function sys_publish(eventName, detail) {
var evt = new CustomEvent(eventName, {
bubbles: true,
cancelable: false,
detail: detail
});
window.dispatchEvent(evt);
},
/**
* XXX: FtuLauncher should register 'isFtuRunning' service.
*/
get runningFTU() {
if ('undefined' === typeof window.FtuLauncher) {
return false;
} else {
return window.FtuLauncher.isFtuRunning();
}
},
/**
* XXX: LockscreenWindowManager should register 'locked' service.
*/
get locked() {
return false;
},
get manifestURL() {
return window.location.href.replace('index.html', 'manifest.webapp');
}
};
})(window);