    /** * This class is used to send requests to the server using {@link Ext.direct.Manager Ext Direct}. * When a request is made, the transport mechanism is handed off to the appropriate * {@link Ext.direct.RemotingProvider Provider} to complete the call. * * # Specifying the functions * * This proxy expects Direct remoting method to be passed in order to be able to complete requests, * one Direct function per CRUD method. This is done via {@link #api} configuration: * *      api: { *          read: 'MyApp.readRecords', *          create: 'MyApp.createRecords', *          update: 'MyApp.updateRecords', *          destroy: 'MyApp.destroyRecords' *      } * * You can also use a `prefix` config to avoid duplicating full namespaces for Direct functions: * *      api: { *          prefix: 'MyApp', *          read: 'readRecords', *          create: 'createRecords', *          update: 'updateRecords', *          destroy: 'destroyRecords' *      } * * The preferred way is to specify function names to allow late resolution, however you can * pass function references instead if desired: * *      api: { *          read: MyApp.readRecords, *          create: MyApp.createRecords, *          update: MyApp.updateRecords, *          destroy: MyApp.destroyRecords *      } * * This method of configuring API is not recommended because this way the Direct functions * need to be created very early in the application lifecycle, long before {@link Ext.app.Application} * instance is initialized. * * You can also use the {@link #directFn} configuration instead of {@link #api}. This will use * the same Direct function for all types of requests. * * # Server API * * The server side methods are expected to conform to the following calling conventions: * * ## `read` * * Accept one argument which is either named arguments in an object (default), or an array * of values depending on the {@link #paramsAsHash} configuration. Return an array of records * or an object with format recognizable by the configured {@link Ext.data.reader.Reader} * instance. * * Example {@link Ext.directRemotingProvider#actions Direct API declaration}: * *      actions: { *          MyApp: [{ *              name: 'readRecords', *              params: [], *              strict: false *          }] *      } * * Example function invocation: * *      MyApp.readRecords( *          { *              start: 0, *              limit: 10 *          }, *          // Results are passed to the callback function *          function(records) { *              console.log(records); *              // Logs:  [{ id: 'r0', text: 'foo' }, { id: 'r1', text: 'bar' }] *          } *      ); * * ## `create` * * Accept one ordered argument which is either an object with data for the new record, * or an array of objects for multiple records. Return an array of identifiers for actually * created records. See {@link Ext.data.Model#clientIdProperty} for more information. * * Example {@link Ext.directRemotingProvider#actions Direct API declaration}: * *      actions: [ *          MyApp: [{ *              name: 'createRecords', *              len: 1 *          }] *      } * * Example function invocation: * *      MyApp.createRecords( *          [ *              { id: 0, text: 'foo' }, *              { id: 1, text: 'bar' } *          ], *          // Results are passed to the callback function *          function(records) { *              console.log(records); *              // Logs: [{ clientId: 0, id: 'r0' }, { clientId: 1, id: 'r1' }] *          } *      ); * * ## `update` * * Accept one ordered argument which is either an object with updated data and valid * record identifier, or an array of objects for multiple records. Return an array of * objects with updated record data. * * Example {@link Ext.directRemotingProvider#actions Direct API declaration}: * *      actions: [ *          MyApp: [{ *              name: 'updateRecords', *              len: 1 *          }] *      } * * Example function invocation: * *      MyApp.updateRecords( *          [ *              { id: 'r0', text: 'blerg' }, *              { id: 'r1', text: 'throbbe' } *          ], *          // Results are passed to the callback function *          function(records) { *              console.log(records); *              // Logs: [{ id: 'r0', text: 'blerg' }, { id: 'r1', text: 'throbbe }] *          } *      ); * * ## `destroy` * * Accept one ordered argument which is an array of record identifiers to be deleted. * Return an object with at least one {@link Ext.data.reader.Json#successProperty} * property set to `true` or `false`, with more optional properties recognizable by configured * {@link Ext.data.reader.Reader} instance. * * Example {@link Ext.directRemotingProvider#actions Direct API declaration}: * *      actions: [ *          MyApp: [{ *              name: 'destroyRecords', *              len: 1 *          }] *      } * * Example function invocation: * *      MyApp.destroyRecords( *          [ *              { id: 'r0' }, *              { id: 'r1' } *          ], *          // Results are passed to the callback function *          function(result) { *              // Default successProperty is `success` *              if (!result.success) { *                  // Handle the error *              } *          } *      ); * * ## Read method parameters * * Direct proxy provides options to help configure which parameters will be sent to the server * for Read operations. By setting the {@link #paramsAsHash} option to `true`, the proxy will * send an object literal containing each of the passed parameters. This is the default. When * {@link #paramsAsHash} is set to `false`, Proxy will pass the Read function an array of values * instead of an object, with the order determined by {@link #paramOrder} value. * * Setting {@link #paramOrder} to any value other than `undefined` will automatically reset * {@link #paramsAsHash} to `false`. * * # Example Usage * *      Ext.define('User', { *          extend: 'Ext.data.Model', *          fields: ['firstName', 'lastName'] *      }); *       *      Ext.define('Users', { *          extend: 'Ext.data.Store', *          model: 'User', *          proxy: { *              type: 'direct', *              directFn: 'MyApp.getUsers', *              // Tells the proxy to pass `start` and `limit` as two by-position arguments: *              paramOrder: 'start,limit' *          } *      }); *       *      var store = new Users(); *      store.load(); */Ext.define('Ext.data.proxy.Direct', {    /* Begin Definitions */     extend: 'Ext.data.proxy.Server',    alternateClassName: 'Ext.data.DirectProxy',     alias: 'proxy.direct',     requires: ['Ext.direct.Manager'],     /* End Definitions */     /**     * @cfg url     * @hide     */     config: {        /**        * @cfg {String/String[]} paramOrder        * A list of params to be passed to server side Read function. Specify the params        * in the order in which they must be executed on the server-side as either (1) an Array        * of String values, or (2) a String of params delimited by either whitespace, comma,        * or pipe. For example, any of the following would be acceptable:        *        *     paramOrder: ['param1','param2','param3']        *     paramOrder: 'param1 param2 param3'        *     paramOrder: 'param1,param2,param3'        *     paramOrder: 'param1|param2|param'        */        paramOrder: undefined,         /**        * @cfg {Boolean} paramsAsHash         * Send Read function parameters as a collection of named arguments. Providing a        * {@link #paramOrder} nullifies this configuration.        */        paramsAsHash: true,         /**        * @cfg {Function/String} directFn        * Function to call when executing a request. `directFn` is a simple alternative to defining        * the api configuration parameter for Stores which will not implement a full CRUD api.        * The `directFn` may also be a string reference to the fully qualified name of the function,        * for example: `'MyApp.company.GetProfile'`. This can be useful when using dynamic loading.        * The string will be resolved before calling the function for the first time.        */        directFn: undefined,         /**        * @cfg {Object} api         * The same as {@link Ext.data.proxy.Server#api}, however instead of providing urls        * you should provide a Direct function name for each CRUD method.        *        * Instead of providing fully qualified names for each function, you can use `prefix`        * property to provide a common prefix for all functions:        *        *   api: {        *       prefix: 'MyApp',        *       read: 'readRecords',        *       create: 'createRecords',        *       update: 'updateRecords',        *       destroy: 'destroyRecords'        *   }        *        * This way function names will be resolved to `'MyApp.readRecords'`, `'MyApp.createRecords'`,        * etc. Note that using `prefix` and fully qualified function names is **not** supported,        * and prefix will be used for every function name when configured.        *        * See also {@link #directFn}.        */        api: undefined,                /**         * @cfg {Object/Array} [metadata]         * Optional set of fixed parameters to send with every Proxy request, similar to         * {@link #extraParams} but available with all CRUD requests. Also unlike         * {@link #extraParams}, metadata is not mixed with the ordinary data but sent         * separately in the data packet.         * You may need to update your server side Ext Direct stack to use this feature.         */        metadata: undefined    },     /**     * @private     */    paramOrderRe: /[\s,|]/,        constructor: function(config) {        this.callParent([config]);        this.canceledOperations = {};    },        applyParamOrder: function(paramOrder) {        if (Ext.isString(paramOrder)) {            paramOrder = paramOrder.split(this.paramOrderRe);        }        return paramOrder;    },     updateApi: function() {        this.methodsResolved = false;    },     updateDirectFn: function() {        this.methodsResolved = false;    },        resolveMethods: function() {        var me = this,            fn = me.getDirectFn(),            api = me.getApi(),            method;                if (fn) {            me.setDirectFn(method = Ext.direct.Manager.parseMethod(fn));                        if (!Ext.isFunction(method)) {                Ext.raise('Cannot resolve directFn ' + fn);            }        }                if (api) {            api = Ext.direct.Manager.resolveApi(api, me);            me.setApi(api);        }                me.methodsResolved = true;    },     doRequest: function(operation) {        var me = this,            writer, request, action, params, args, api, fn, callback;                if (!me.methodsResolved) {            me.resolveMethods();        }                request = me.buildRequest(operation);        action  = request.getAction();        api     = me.getApi();         if (api) {            fn = api[action];        }                fn = fn || me.getDirectFn();                //<debug>         if (!fn || !fn.directCfg) {            Ext.raise({                msg: 'No Ext Direct function specified for Direct proxy "' + action + '" operation',                proxy: me            });        }                // This might lead to exceptions so bail out early         if (!me.paramOrder && fn.directCfg.method.len > 1) {            Ext.raise({                msg: 'Incorrect parameters for Direct proxy "' + action + '" operation',                proxy: me            });        }        //</debug>                 writer = me.getWriter();         if (writer && operation.allowWrite()) {            request = writer.write(request);        }                // The weird construct below is due to historical way of handling extraParams;         // they were mixed in with request data in ServerProxy.buildRequest() and were         // inseparable after that point. This does not work well with CUD operations         // so instead of using potentially poisoned request params we took the raw         // JSON data as Direct function argument payload (but only for CUD!). A side         // effect of that was that the request metadata (extraParams) was only available         // for read operations.         // We keep this craziness for backwards compatibility.         if (action === 'read') {            params = request.getParams();        }        else {            params = request.getJsonData();        }                args = fn.directCfg.method.getArgs({            params: params,            allowSingle: writer.getAllowSingle(),            paramOrder: me.getParamOrder(),            paramsAsHash: me.getParamsAsHash(),            paramsAsArray: true,            metadata: me.getMetadata(),            callback: me.createRequestCallback(request, operation),            scope: me        });                request.setConfig({            args: args,            directFn: fn        });                fn.apply(window, args);                // Store expects us to return something to indicate that the request         // is pending; not doing so will make a buffered Store repeat the         // requests over and over.         return request;    },        /**     * Aborts a running request by operation.     *     * @param {Ext.data.Operation} operation The operation to abort. This parameter     * is mandatory.     */    abort: function(operation) {        var id;                // Assume this can be called with request instead of operation, a la Ajax proxy         if (operation && operation.isDataRequest) {            operation = operation.getOperation();        }                // Check definedness again, the above could have returned null         if (operation && operation.isOperation) {            id = operation.id;        }                // We cannot abort a running request but we can ignore the data when it comes back.         if (id != null) {            this.canceledOperations[id] = true;        }    },     /**     * @method     * @inheritdoc     */    applyEncoding: Ext.identityFn,     createRequestCallback: function(request, operation) {        var me = this;         return function(data, event) {            if (!me.canceledOperations[operation.id]) {                me.processResponse(event.status, operation, request, event);            }                        delete me.canceledOperations[operation.id];        };    },     /**     * @method     * @inheritdoc     */    extractResponseData: function(response) {        return Ext.isDefined(response.result) ? response.result : response.data;    },     /**     * @method     * @inheritdoc     */    setException: function(operation, response) {        operation.setException(response.message);    },     /**     * @method     * @inheritdoc     */    buildUrl: function() {        return '';    }});