    /** * ServerProxy is a superclass of {@link Ext.data.proxy.JsonP JsonPProxy} and {@link Ext.data.proxy.Ajax AjaxProxy}, and * would not usually be used directly. * @protected */Ext.define('Ext.data.proxy.Server', {    extend: 'Ext.data.proxy.Proxy',    alias : 'proxy.server',    alternateClassName: 'Ext.data.ServerProxy',    uses  : ['Ext.data.Request'],        isRemote: true,     config: {        /**         * @cfg {String} url          * The URL from which to request the data object.         */        url: '',            /**         * @cfg {String} [pageParam="page"]         * The name of the 'page' parameter to send in a request. Defaults to 'page'. Set this to `''` if you don't         * want to send a page parameter.         */        pageParam: 'page',            /**         * @cfg {String} [startParam="start"]         * The name of the 'start' parameter to send in a request. Defaults to 'start'. Set this to `''` if you don't         * want to send a start parameter.         */        startParam: 'start',            /**         * @cfg {String} [limitParam="limit"]         * The name of the 'limit' parameter to send in a request. Defaults to 'limit'. Set this to `''` if you don't         * want to send a limit parameter.         */        limitParam: 'limit',            /**         * @cfg {String} [groupParam="group"]         * The name of the 'group' parameter to send in a request. Defaults to 'group'. Set this to `''` if you don't         * want to send a group parameter.         */        groupParam: 'group',            /**         * @cfg {String} [groupDirectionParam="groupDir"]         * The name of the direction parameter to send in a request. **This is only used when simpleGroupMode is set to         * true.**         */        groupDirectionParam: 'groupDir',            /**         * @cfg {String} [sortParam="sort"]         * The name of the 'sort' parameter to send in a request. Defaults to 'sort'. Set this to `''` if you don't         * want to send a sort parameter.         */        sortParam: 'sort',            /**         * @cfg {String} [filterParam="filter"]         * The name of the 'filter' parameter to send in a request. Defaults to 'filter'. Set this to `''` if you don't         * want to send a filter parameter.         */        filterParam: 'filter',            /**         * @cfg {String} [directionParam="dir"]         * The name of the direction parameter to send in a request. **This is only used when simpleSortMode is set to         * true.**         */        directionParam: 'dir',            /**         * @cfg {String} [idParam="id"]         * The name of the parameter which carries the id of the entity being operated upon.         */        idParam: 'id',            /**         * @cfg {Boolean} [simpleSortMode=false]         * Enabling simpleSortMode in conjunction with remoteSort will only send one sort property and a direction when a         * remote sort is requested. The {@link #directionParam} and {@link #sortParam} will be sent with the property name         * and either 'ASC' or 'DESC'.         */        simpleSortMode: false,            /**         * @cfg {Boolean} [simpleGroupMode=false]         * Enabling simpleGroupMode in conjunction with remoteGroup will only send one group property and a direction when a         * remote group is requested. The {@link #groupDirectionParam} and {@link #groupParam} will be sent with the property name and either 'ASC'         * or 'DESC'.         */        simpleGroupMode: false,            /**         * @cfg {Boolean} [noCache=true]         * Disable caching by adding a unique parameter name to the request. Set to false to allow caching. Defaults to true.         */        noCache : true,            /**         * @cfg {String} [cacheString="_dc"]         * The name of the cache param added to the url when using noCache. Defaults to "_dc".         */        cacheString: "_dc",            /**         * @cfg {Number} timeout          * The number of milliseconds to wait for a response. Defaults to 30000 milliseconds (30 seconds).         */        timeout : 30000,            /**         * @cfg {Object} api          * Specific urls to call on CRUD action methods "create", "read", "update" and "destroy". Defaults to:         *         *     api: {         *         create  : undefined,         *         read    : undefined,         *         update  : undefined,         *         destroy : undefined         *     }         *         * The url is built based upon the action being executed [create|read|update|destroy] using the commensurate         * {@link #api} property, or if undefined default to the configured         * {@link Ext.data.Store}.{@link Ext.data.proxy.Server#url url}.         *         * For example:         *         *     api: {         *         create  : '/controller/new',         *         read    : '/controller/load',         *         update  : '/controller/update',         *         destroy : '/controller/destroy_action'         *     }         *         * If the specific URL for a given CRUD action is undefined, the CRUD action request will be directed to the         * configured {@link Ext.data.proxy.Server#url url}.         */        api: {            create  : undefined,            read    : undefined,            update  : undefined,            destroy : undefined        },         /**         * @cfg {Object} extraParams          * Extra parameters that will be included on every request. Individual requests with params of the same name         * will override these params when they are in conflict.         */        extraParams: {}    },     /**     * @event exception     * Fires when the server returns an exception. This event may also be listened     * to in the event that a request has timed out or has been aborted.     * @param {Ext.data.proxy.Proxy} this     * @param {Ext.data.Request} request The request that was sent     * @param {Ext.data.operation.Operation} operation The operation that triggered the request     */     //in a ServerProxy all four CRUD operations are executed in the same manner, so we delegate to doRequest in each case     create: function() {        return this.doRequest.apply(this, arguments);    },     read: function() {        return this.doRequest.apply(this, arguments);    },     update: function() {        return this.doRequest.apply(this, arguments);    },     erase: function() {        return this.doRequest.apply(this, arguments);    },     /**     * Sets a value in the underlying {@link #extraParams}.     * @param {String} name The key for the new value     * @param {Object} value The value     */    setExtraParam: function(name, value) {        var extraParams = this.getExtraParams();        extraParams[name] = value;        this.fireEvent('extraparamschanged', extraParams);    },     updateExtraParams: function(newExtraParams, oldExtraParams) {        this.fireEvent('extraparamschanged', newExtraParams);    },     /**     * Creates an {@link Ext.data.Request Request} object from {@link Ext.data.operation.Operation Operation}.     *     * This gets called from doRequest methods in subclasses of Server proxy.     *      * @param {Ext.data.operation.Operation} operation The operation to execute     * @return {Ext.data.Request} The request object     */    buildRequest: function(operation) {        var me = this,            initialParams = Ext.apply({}, operation.getParams()),            // Clone params right now so that they can be mutated at any point further down the call stack             params = Ext.applyIf(initialParams, me.getExtraParams() || {}),            request,            operationId,            idParam;         //copy any sorters, filters etc into the params so they can be sent over the wire         Ext.applyIf(params, me.getParams(operation));         // Set up the entity id parameter according to the configured name.         // This defaults to "id". But TreeStore has a "nodeParam" configuration which         // specifies the id parameter name of the node being loaded.         operationId = operation.getId();        idParam = me.getIdParam();        if (operationId !== undefined && params[idParam] === undefined) {            params[idParam] = operationId;        }         request = new Ext.data.Request({            params   : params,            action   : operation.getAction(),            records  : operation.getRecords(),            url      : operation.getUrl(),            operation: operation,             // this is needed by JsonSimlet in order to properly construct responses for             // requests from this proxy             proxy: me        });         request.setUrl(me.buildUrl(request));         /*         * Save the request on the Operation. Operations don't usually care about Request and Response data, but in the         * ServerProxy and any of its subclasses we add both request and response as they may be useful for further processing         */        operation.setRequest(request);         return request;    },     /**     * Processes response, which may involve updating or committing records, each of which     * will inform the owning stores and their interested views. Finally, we may perform     * an additional layout if the data shape has changed.      *     * @protected     */    processResponse: function(success, operation, request, response) {        var me = this,            exception, reader, resultSet, meta;                // Async callback could have landed at any time, including during and after         // destruction. We don't want to unravel the whole response chain in such case.         if (me.destroying || me.destroyed) {            return;        }         // Processing a response may involve updating or committing many records         // each of which will inform the owning stores, which will ultimately         // inform interested views which will most likely have to do a layout         // assuming that the data shape has changed.         // Bracketing the processing with this event gives owning stores the ability         // to fire their own beginupdate/endupdate events which can be used by interested         // views to suspend layouts.         me.fireEvent('beginprocessresponse', me, response, operation);         if (success === true) {            reader = me.getReader();             if (response.status === 204) {                resultSet = reader.getNullResultSet();            } else {                resultSet = reader.read(me.extractResponseData(response), {                    // If we're doing an update, we want to construct the models ourselves.                     recordCreator: operation.getRecordCreator()                });            }             operation.process(resultSet, request, response);            exception = !operation.wasSuccessful();        } else {            me.setException(operation, response);            exception = true;        }                if (exception) {            me.fireEvent('exception', me, response, operation);        }         // If a JsonReader detected metadata, process it now.         // This will fire the 'metachange' event which the Store processes to fire its own 'metachange'         else {            meta = resultSet.getMetadata();            if (meta) {                me.onMetaChange(meta);            }        }         me.afterRequest(request, success);         // Tell owning store processing has finished.         // It will fire its endupdate event which will cause interested views to          // resume layouts.         me.fireEvent('endprocessresponse', me, response, operation);    },        /**     * Sets up an exception on the operation     * @private     * @param {Ext.data.operation.Operation} operation The operation     * @param {Object} response The response     */    setException: function(operation, response) {        operation.setException({            status: response.status,            statusText: response.statusText,            response: response        });    },     /**     * @method     * Template method to allow subclasses to specify how to get the response for the reader.     * @template     * @private     * @param {Object} response The server response     * @return {Object} The response data to be used by the reader     */    extractResponseData: Ext.identityFn,     /**     * Encode any values being sent to the server. Can be overridden in subclasses.     * @protected     * @param {Array} value An array of sorters/filters.     * @return {Object} The encoded value     */    applyEncoding: function(value) {        return Ext.encode(value);    },     /**     * Encodes the array of {@link Ext.util.Sorter} objects into a string to be sent in the request url. By default,     * this simply JSON-encodes the sorter data     * @param {Ext.util.Sorter[]} sorters The array of {@link Ext.util.Sorter Sorter} objects     * @param {Boolean} [preventArray=false] Prevents the items from being output as an array.     * @return {String} The encoded sorters     */    encodeSorters: function(sorters, preventArray) {        var out = [],            length = sorters.length,             i;                for (i = 0; i < length; i++) {            out[i] = sorters[i].serialize();        }         return this.applyEncoding(preventArray ? out[0] : out);    },     /**     * Encodes the array of {@link Ext.util.Filter} objects into a string to be sent in the request url. By default,     * this simply JSON-encodes the filter data     * @param {Ext.util.Filter[]} filters The array of {@link Ext.util.Filter Filter} objects     * @return {String} The encoded filters     */    encodeFilters: function (filters) {        var out = [],            length = filters.length,            i, filter;         for (i = 0; i < length; i++) {            filter = filters[i];             // Filters with a custom filterFn cannot be serialized.  But since #getFilterFn()             // always returns a filterFn, we need to check if it's been generated by default.             // If so, we know that the filter cannot have a custom filterFn defined, and it             // is therefore okay to serialize.             filter.getFilterFn();            if (filter.generatedFilterFn) {                out.push(filter.serialize());            }        }         return this.applyEncoding(out);    },     /**     * @private     * Copy any sorters, filters etc into the params so they can be sent over the wire     */    getParams: function(operation) {        if (!operation.isReadOperation) {            return {};        }         var me = this,            params = {},            grouper = operation.getGrouper(),            sorters = operation.getSorters(),            filters = operation.getFilters(),            page = operation.getPage(),            start = operation.getStart(),            limit = operation.getLimit(),            simpleSortMode = me.getSimpleSortMode(),            simpleGroupMode = me.getSimpleGroupMode(),            pageParam = me.getPageParam(),            startParam = me.getStartParam(),            limitParam = me.getLimitParam(),            groupParam = me.getGroupParam(),            groupDirectionParam = me.getGroupDirectionParam(),            sortParam = me.getSortParam(),            filterParam = me.getFilterParam(),            directionParam = me.getDirectionParam(),            hasGroups, index;         if (pageParam && page) {            params[pageParam] = page;        }         if (startParam && (start || start === 0)) {            params[startParam] = start;        }         if (limitParam && limit) {            params[limitParam] = limit;        }         hasGroups = groupParam && grouper;        if (hasGroups) {            // Grouper is a subclass of sorter, so we can just use the sorter method             if (simpleGroupMode) {                params[groupParam] = grouper.getProperty();                params[groupDirectionParam] = grouper.getDirection();            } else {                params[groupParam] = me.encodeSorters([grouper], true);            }        }         if (sortParam && sorters && sorters.length > 0) {            if (simpleSortMode) {                index = 0;                // Group will be included in sorters, so grab the next one                 if (sorters.length > 1 && hasGroups) {                    index = 1;                }                params[sortParam] = sorters[index].getProperty();                params[directionParam] = sorters[index].getDirection();            } else {                params[sortParam] = me.encodeSorters(sorters);            }         }         if (filterParam && filters && filters.length > 0) {            params[filterParam] = me.encodeFilters(filters);        }         return params;    },     /**     * Generates a url based on a given Ext.data.Request object. By default, ServerProxy's buildUrl will add the     * cache-buster param to the end of the url. Subclasses may need to perform additional modifications to the url.     * @param {Ext.data.Request} request The request object     * @return {String} The url     */    buildUrl: function(request) {        var me = this,            url = me.getUrl(request);         //<debug>         if (!url) {            Ext.raise("You are using a ServerProxy but have not supplied it with a url.");        }        //</debug>          if (me.getNoCache()) {            url = Ext.urlAppend(url, Ext.String.format("{0}={1}", me.getCacheString(), Ext.Date.now()));        }         return url;    },     /**     * Get the url for the request taking into account the order of priority,     * - The request     * - The api     * - The url     * @private     * @param {Ext.data.Request} request The request     * @return {String} The url     */    getUrl: function(request) {        var url;        if (request) {            url = request.getUrl() || this.getApi()[request.getAction()];        }        return url ? url : this.callParent();    },     /**     * In ServerProxy subclasses, the {@link #create}, {@link #read}, {@link #update} and {@link #erase} methods all     * pass through to doRequest. Each ServerProxy subclass must implement the doRequest method - see {@link     * Ext.data.proxy.JsonP} and {@link Ext.data.proxy.Ajax} for examples. This method carries the same signature as     * each of the methods that delegate to it.     *     * @param {Ext.data.operation.Operation} operation The Ext.data.operation.Operation object     * @param {Function} callback The callback function to call when the Operation has completed     * @param {Object} scope The scope in which to execute the callback     */    doRequest: function(operation) {        //<debug>         Ext.raise("The doRequest function has not been implemented on your Ext.data.proxy.Server subclass. See src/data/ServerProxy.js for details");        //</debug>     },     /**     * Optional callback function which can be used to clean up after a request has been completed.     * @param {Ext.data.Request} request The Request object     * @param {Boolean} success True if the request was successful     * @protected     * @template     * @method     */    afterRequest: Ext.emptyFn,     destroy: function() {        Ext.destroy(this.getReader(), this.getWriter());        this.reader = this.writer = null;                this.callParent();    }});