    /** * This is a base class for more advanced "simlets" (simulated servers). A simlet is asked * to provide a response given a {@link Ext.ux.ajax.SimXhr} instance. */Ext.define('Ext.ux.ajax.Simlet', function () {    var urlRegex = /([^?#]*)(#.*)?$/,        dateRegex = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/,        intRegex = /^[+-]?\d+$/,        floatRegex = /^[+-]?\d+\.\d+$/;     function parseParamValue (value) {        var m;         if (Ext.isDefined(value)) {            value = decodeURIComponent(value);             if (intRegex.test(value)) {                value = parseInt(value, 10);            } else if (floatRegex.test(value)) {                value = parseFloat(value);            } else if (!!(m = dateRegex.exec(value))) {                value = new Date(Date.UTC(+m[1], +m[2]-1, +m[3], +m[4], +m[5], +m[6]));            }        }         return value;    }     return {        alias: 'simlet.basic',         isSimlet: true,         responseProps: ['responseText', 'responseXML', 'status', 'statusText', 'responseHeaders'],         /**         * @cfg {String/Function} responseText         */         /**         * @cfg {String/Function} responseXML         */         /**         * @cfg {Object/Function} responseHeaders         */         /**         * @cfg {Number/Function} status         */        status: 200,         /**         * @cfg {String/Function} statusText         */        statusText: 'OK',         constructor: function (config) {            Ext.apply(this, config);        },         doGet: function (ctx) {            return this.handleRequest(ctx);        },                doPost: function (ctx) {            return this.handleRequest(ctx);        },         doRedirect: function (ctx) {            return false;        },         doDelete: function (ctx) {            var me = this,                xhr = ctx.xhr,                records = xhr.options.records;            me.removeFromData(ctx,records);        },         /**         * Performs the action requested by the given XHR and returns an object to be applied         * on to the XHR (containing `status`, `responseText`, etc.). For the most part,         * this is delegated to `doMethod` methods on this class, such as `doGet`.         *         * @param {Ext.ux.ajax.SimXhr} xhr The simulated XMLHttpRequest instance.         * @return {Object} The response properties to add to the XMLHttpRequest.         */        exec: function (xhr) {            var me = this,                ret = {},                method = 'do' + Ext.String.capitalize(xhr.method.toLowerCase()), // doGet                 fn = me[method];             if (fn) {                ret = fn.call(me, me.getCtx(xhr.method, xhr.url, xhr));            } else {                ret = { status: 405, statusText: 'Method Not Allowed' };            }             return ret;        },         getCtx: function (method, url, xhr) {            return {                method: method,                params: this.parseQueryString(url),                url: url,                xhr: xhr            };        },         handleRequest: function(ctx) {            var me = this,                ret = {},                val;             Ext.Array.forEach(me.responseProps, function (prop) {                if (prop in me) {                    val = me[prop];                    if (Ext.isFunction(val)) {                        val = val.call(me, ctx);                    }                    ret[prop] = val;                }            });             return ret;        },         openRequest: function (method, url, options, async) {            var ctx = this.getCtx(method, url),                redirect = this.doRedirect(ctx),                xhr;            if (options.action === 'destroy'){                method = 'delete';            }            if (redirect) {                xhr = redirect;            } else {                xhr = new Ext.ux.ajax.SimXhr({                    mgr: this.manager,                    simlet: this,                    options: options                });                xhr.open(method, url, async);            }                        return xhr;        },         parseQueryString : function (str) {            var m = urlRegex.exec(str),                ret = {},                key,                value,                i, n;             if (m && m[1]) {                var pair, parts = m[1].split('&');                 for (i = 0, n = parts.length; i < n; ++i) {                    if ((pair = parts[i].split('='))[0]) {                        key = decodeURIComponent(pair.shift());                        value = parseParamValue((pair.length > 1) ? pair.join('=') : pair[0]);                         if (!(key in ret)) {                            ret[key] = value;                        } else if (Ext.isArray(ret[key])) {                            ret[key].push(value);                        } else {                            ret[key] = [ret[key], value];                        }                    }                }            }             return ret;        },         redirect: function (method, url, params) {            switch (arguments.length) {                case 2:                    if (typeof url == 'string') {                        break;                    }                    params = url;                    // fall...                 case 1:                    url = method;                    method = 'GET';                    break;            }             if (params) {                url = Ext.urlAppend(url, Ext.Object.toQueryString(params));            }            return this.manager.openRequest(method, url);        },         removeFromData: function(ctx, records) {            var me = this,                data = me.getData(ctx),                model = (ctx.xhr.options.proxy && ctx.xhr.options.proxy.getModel()) || {},                idProperty = model.idProperty || 'id';             Ext.each(records, function(record) {                var id = record.get(idProperty);                for (var i = data.length; i-- > 0;) {                    if (data[i][idProperty] === id) {                        me.deleteRecord(i);                        break;                    }                }            });        }    };}());