/** * @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 repetitive 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); } } } } args.__source = document.location.href; var result = this.doSend(args); return (result && result.length > 0) ? JSON.parse(result) : null; }, doSend: function(args) { var xhr = new XMLHttpRequest(); xhr.open('GET', this.SERVER_URL + '?' + Ext.Object.toQueryString(args) + '&_dc=' + new Date().getTime(), false); // wrap the request in a try/catch block so we can check if any errors are thrown and attempt to call any // failure/callback functions if defined try { xhr.send(null); return xhr.responseText; } catch(e) { if (args.failure) { this.invoke(args.failure); } else if (args.callback) { this.invoke(args.callback); } } } });