/**
* @private
*
* This object handles communication between the WebView and Sencha's native shell.
* Currently it has two primary responsibilities:
*
* 1. Maintaining unique string ids for callback functions, together with their scope objects
* 2. Serializing given object data into HTTP GET request parameters
*
* As an example, to capture a photo from the device's camera, we use `Ext.device.Camera.capture()` like:
*
* Ext.device.Camera.capture(
* function(dataUri){
* // Do something with the base64-encoded `dataUri` string
* },
* function(errorMessage) {
*
* },
* callbackScope,
* {
* quality: 75,
* width: 500,
* height: 500
* }
* );
*
* Internally, `Ext.device.Communicator.send()` will then be invoked with the following argument:
*
* Ext.device.Communicator.send({
* command: 'Camera#capture',
* callbacks: {
* onSuccess: function() { ... },
* onError: function() { ... },
* },
* scope: callbackScope,
* quality: 75,
* width: 500,
* height: 500
* });
*
* Which will then be transformed into a HTTP GET request, sent to native shell's local
* HTTP server with the following parameters:
*
* ?quality=75&width=500&height=500&command=Camera%23capture&onSuccess=3&onError=5
*
* Notice that `onSuccess` and `onError` have been converted into string ids (`3` and `5`
* respectively) and maintained by `Ext.device.Communicator`.
*
* Whenever the requested operation finishes, `Ext.device.Communicator.invoke()` simply needs
* to be executed from the native shell with the corresponding ids given before. For example:
*
* Ext.device.Communicator.invoke('3', ['DATA_URI_OF_THE_CAPTURED_IMAGE_HERE']);
*
* will invoke the original `onSuccess` callback under the given scope. (`callbackScope`), with
* the first argument of 'DATA_URI_OF_THE_CAPTURED_IMAGE_HERE'
*
* Note that `Ext.device.Communicator` maintains the uniqueness of each function callback and
* its scope object. If subsequent calls to `Ext.device.Communicator.send()` have the same
* callback references, the same old ids will simply be reused, which guarantee the best possible
* performance for a large amount of repeative calls.
*/
Ext.define('Ext.device.communicator.Default', {
SERVER_URL: 'http://localhost:3000', // Change this to the correct server URL
callbackDataMap: {},
callbackIdMap: {},
idSeed: 0,
globalScopeId: '0',
generateId: function() {
return String(++this.idSeed);
},
getId: function(object) {
var id = object.$callbackId;
if (!id) {
object.$callbackId = id = this.generateId();
}
return id;
},
getCallbackId: function(callback, scope) {
var idMap = this.callbackIdMap,
dataMap = this.callbackDataMap,
id, scopeId, callbackId, data;
if (!scope) {
scopeId = this.globalScopeId;
}
else if (scope.isIdentifiable) {
scopeId = scope.getId();
}
else {
scopeId = this.getId(scope);
}
callbackId = this.getId(callback);
if (!idMap[scopeId]) {
idMap[scopeId] = {};
}
if (!idMap[scopeId][callbackId]) {
id = this.generateId();
data = {
callback: callback,
scope: scope
};
idMap[scopeId][callbackId] = id;
dataMap[id] = data;
}
return idMap[scopeId][callbackId];
},
getCallbackData: function(id) {
return this.callbackDataMap[id];
},
invoke: function(id, args) {
var data = this.getCallbackData(id);
data.callback.apply(data.scope, args);
},
send: function(args) {
var callbacks, scope, name, callback;
if (!args) {
args = {};
}
else if (args.callbacks) {
callbacks = args.callbacks;
scope = args.scope;
delete args.callbacks;
delete args.scope;
for (name in callbacks) {
if (callbacks.hasOwnProperty(name)) {
callback = callbacks[name];
if (typeof callback == 'function') {
args[name] = this.getCallbackId(callback, scope);
}
}
}
}
this.doSend(args);
},
doSend: function(args) {
var xhr = new XMLHttpRequest();
xhr.open('GET', this.SERVER_URL + '?' + Ext.Object.toQueryString(args), false);
xhr.send(null);
}
});