Upgrade to ExtJS 3.0.3 - Released 10/11/2009
[extjs.git] / pkgs / data-foundation-debug.js
index 5076cfc..8164b7d 100644 (file)
@@ -1,9 +1,10 @@
 /*!
 /*!
- * Ext JS Library 3.0.0
+ * Ext JS Library 3.0.3
  * Copyright(c) 2006-2009 Ext JS, LLC
  * licensing@extjs.com
  * http://www.extjs.com/license
  */
  * Copyright(c) 2006-2009 Ext JS, LLC
  * licensing@extjs.com
  * http://www.extjs.com/license
  */
+
 /**
  * @class Ext.data.Api
  * @extends Object
 /**
  * @class Ext.data.Api
  * @extends Object
@@ -184,10 +185,86 @@ new Ext.data.HttpProxy({
             for (var verb in this.restActions) {
                 proxy.api[this.actions[verb]].method = this.restActions[verb];
             }
             for (var verb in this.restActions) {
                 proxy.api[this.actions[verb]].method = this.restActions[verb];
             }
+            // TODO: perhaps move this interceptor elsewhere?  like into DataProxy, perhaps?  Placed here
+            // to satisfy initial 3.0 final release of REST features.
+            proxy.onWrite = proxy.onWrite.createInterceptor(function(action, o, response, rs) {
+                var reader = o.reader;
+                var res = new Ext.data.Response({
+                    action: action,
+                    raw: response
+                });
+
+                switch (response.status) {
+                    case 200:   // standard 200 response, send control back to HttpProxy#onWrite
+                        return true;
+                        break;
+                    case 201:   // entity created but no response returned
+                        //res[reader.meta.successProperty] = true;
+                        res.success = true;
+                        break;
+                    case 204:  // no-content.  Create a fake response.
+                        //res[reader.meta.successProperty] = true;
+                        //res[reader.meta.root] = null;
+                        res.success = true;
+                        res.data = null;
+                        break;
+                    default:
+                        return true;
+                        break;
+                }
+                /*
+                if (res[reader.meta.successProperty] === true) {
+                    this.fireEvent("write", this, action, res[reader.meta.root], res, rs, o.request.arg);
+                } else {
+                    this.fireEvent('exception', this, 'remote', action, o, res, rs);
+                }
+                */
+                if (res.success === true) {
+                    this.fireEvent("write", this, action, res.data, res, rs, o.request.arg);
+                } else {
+                    this.fireEvent('exception', this, 'remote', action, o, res, rs);
+                }
+                o.request.callback.call(o.request.scope, res.data, res, res.success);
+
+                return false;   // <-- false to prevent intercepted function from running.
+            }, proxy);
         }
     };
 })();
 
         }
     };
 })();
 
+/**
+ * Ext.data.Response
+ * Experimental.  Do not use directly.
+ */
+Ext.data.Response = function(params, response) {
+    Ext.apply(this, params, {
+        raw: response
+    });
+};
+Ext.data.Response.prototype = {
+    message : null,
+    success : false,
+    status : null,
+    root : null,
+    raw : null,
+
+    getMessage : function() {
+        return this.message;
+    },
+    getSuccess : function() {
+        return this.success;
+    },
+    getStatus : function() {
+        return this.status
+    },
+    getRoot : function() {
+        return this.root;
+    },
+    getRawResponse : function() {
+        return this.raw;
+    }
+};
+
 /**
  * @class Ext.data.Api.Error
  * @extends Ext.Error
 /**
  * @class Ext.data.Api.Error
  * @extends Ext.Error
@@ -208,6 +285,8 @@ Ext.apply(Ext.data.Api.Error.prototype, {
         'execute': 'Attempted to execute an unknown action.  Valid API actions are defined in Ext.data.Api.actions"'
     }
 });
         'execute': 'Attempted to execute an unknown action.  Valid API actions are defined in Ext.data.Api.actions"'
     }
 });
+
+
 \r
 /**\r
  * @class Ext.data.SortTypes\r
 \r
 /**\r
  * @class Ext.data.SortTypes\r
@@ -309,8 +388,9 @@ Ext.data.SortTypes = {
  * <code>{@link #data}</code> and <code>{@link #id}</code> properties.</p>
  * <p>Record objects generated by this constructor inherit all the methods of Ext.data.Record listed below.</p>
  * @constructor
  * <code>{@link #data}</code> and <code>{@link #id}</code> properties.</p>
  * <p>Record objects generated by this constructor inherit all the methods of Ext.data.Record listed below.</p>
  * @constructor
- * This constructor should not be used to create Record objects. Instead, use {@link #create} to
- * generate a subclass of Ext.data.Record configured with information about its constituent fields.
+ * <p>This constructor should not be used to create Record objects. Instead, use {@link #create} to
+ * generate a subclass of Ext.data.Record configured with information about its constituent fields.<p>
+ * <p><b>The generated constructor has the same signature as this constructor.</b></p>
  * @param {Object} data (Optional) An object, the properties of which provide values for the new Record's
  * fields. If not specified the <code>{@link Ext.data.Field#defaultValue defaultValue}</code>
  * for each field will be assigned.
  * @param {Object} data (Optional) An object, the properties of which provide values for the new Record's
  * fields. If not specified the <code>{@link Ext.data.Field#defaultValue defaultValue}</code>
  * for each field will be assigned.
@@ -361,7 +441,7 @@ myStore.{@link Ext.data.Store#add add}(myNewRecord);
 </code></pre>
  * @method create
  * @return {function} A constructor which is used to create new Records according
 </code></pre>
  * @method create
  * @return {function} A constructor which is used to create new Records according
- * to the definition. The constructor has the same signature as {@link #Ext.data.Record}.
+ * to the definition. The constructor has the same signature as {@link #Record}.
  * @static
  */
 Ext.data.Record.create = function(o){
  * @static
  */
 Ext.data.Record.create = function(o){
@@ -813,6 +893,9 @@ var recId = 100; // provide unique id for the record
 var r = new myStore.recordType(defaultData, ++recId); // create new record
 myStore.{@link #insert}(0, r); // insert a new record into the store (also see {@link #add})
  * </code></pre>
 var r = new myStore.recordType(defaultData, ++recId); // create new record
 myStore.{@link #insert}(0, r); // insert a new record into the store (also see {@link #add})
  * </code></pre>
+ * <p><u>Writing Data</u></p>
+ * <p>And <b>new in Ext version 3</b>, use the new {@link Ext.data.DataWriter DataWriter} to create an automated, <a href="http://extjs.com/deploy/dev/examples/writer/writer.html">Writable Store</a>
+ * along with <a href="http://extjs.com/deploy/dev/examples/restful/restful.html">RESTful features.</a>
  * @constructor
  * Creates a new Store.
  * @param {Object} config A config object containing the objects needed for the Store to access data,
  * @constructor
  * Creates a new Store.
  * @param {Object} config A config object containing the objects needed for the Store to access data,
@@ -841,7 +924,7 @@ Ext.data.Store = function(config){
     }
 
     Ext.apply(this, config);
     }
 
     Ext.apply(this, config);
-    
+
     this.paramNames = Ext.applyIf(this.paramNames || {}, this.defaultParamNames);
 
     if(this.url && !this.proxy){
     this.paramNames = Ext.applyIf(this.paramNames || {}, this.defaultParamNames);
 
     if(this.url && !this.proxy){
@@ -859,7 +942,8 @@ Ext.data.Store = function(config){
             this.recordType = this.reader.recordType;
         }
         if(this.reader.onMetaChange){
             this.recordType = this.reader.recordType;
         }
         if(this.reader.onMetaChange){
-            this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
+            //this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
+            this.reader.onMetaChange = this.reader.onMetaChange.createSequence(this.onMetaChange, this);
         }
         if (this.writer) { // writer passed
             this.writer.meta = this.reader.meta;
         }
         if (this.writer) { // writer passed
             this.writer.meta = this.reader.meta;
@@ -991,6 +1075,7 @@ var grid = new Ext.grid.EditorGridPanel({
          * @event clear
          * Fires when the data cache has been cleared.
          * @param {Store} this
          * @event clear
          * Fires when the data cache has been cleared.
          * @param {Store} this
+         * @param {Record[]} The records that were cleared.
          */
         'clear',
         /**
          */
         'clear',
         /**
@@ -1032,7 +1117,7 @@ var grid = new Ext.grid.EditorGridPanel({
         'loadexception',
         /**
          * @event beforewrite
         'loadexception',
         /**
          * @event beforewrite
-         * @param {DataProxy} this
+         * @param {Ext.data.Store} store
          * @param {String} action [Ext.data.Api.actions.create|update|destroy]
          * @param {Record/Array[Record]} rs
          * @param {Object} options The loading options that were specified. Edit <code>options.params</code> to add Http parameters to the request.  (see {@link #save} for details)
          * @param {String} action [Ext.data.Api.actions.create|update|destroy]
          * @param {Record/Array[Record]} rs
          * @param {Object} options The loading options that were specified. Edit <code>options.params</code> to add Http parameters to the request.  (see {@link #save} for details)
@@ -1042,9 +1127,8 @@ var grid = new Ext.grid.EditorGridPanel({
         /**
          * @event write
          * Fires if the server returns 200 after an Ext.data.Api.actions CRUD action.
         /**
          * @event write
          * Fires if the server returns 200 after an Ext.data.Api.actions CRUD action.
-         * Success or failure of the action is available in the <code>result['successProperty']</code> property.
-         * The server-code might set the <code>successProperty</code> to <tt>false</tt> if a database validation
-         * failed, for example.
+         * Success of the action is determined in the <code>result['successProperty']</code>property (<b>NOTE</b> for RESTful stores,
+         * a simple 20x response is sufficient for the actions "destroy" and "update".  The "create" action should should return 200 along with a database pk).
          * @param {Ext.data.Store} store
          * @param {String} action [Ext.data.Api.actions.create|update|destroy]
          * @param {Object} result The 'data' picked-out out of the response for convenience.
          * @param {Ext.data.Store} store
          * @param {String} action [Ext.data.Api.actions.create|update|destroy]
          * @param {Object} result The 'data' picked-out out of the response for convenience.
@@ -1063,7 +1147,8 @@ var grid = new Ext.grid.EditorGridPanel({
             scope: this,
             add: this.createRecords,
             remove: this.destroyRecord,
             scope: this,
             add: this.createRecords,
             remove: this.destroyRecord,
-            update: this.updateRecord
+            update: this.updateRecord,
+            clear: this.onClear
         });
     }
 
         });
     }
 
@@ -1241,7 +1326,7 @@ sortInfo: {
      * internally be set to <tt>false</tt>.</p>
      */
     restful: false,
      * internally be set to <tt>false</tt>.</p>
      */
     restful: false,
-    
+
     /**
      * @cfg {Object} paramNames
      * <p>An object containing properties which specify the names of the paging and
     /**
      * @cfg {Object} paramNames
      * <p>An object containing properties which specify the names of the paging and
@@ -1261,7 +1346,7 @@ sortInfo: {
      * the parameter names to use in its {@link #load requests}.
      */
     paramNames : undefined,
      * the parameter names to use in its {@link #load requests}.
      */
     paramNames : undefined,
-    
+
     /**
      * @cfg {Object} defaultParamNames
      * Provides the default values for the {@link #paramNames} property. To globally modify the parameters
     /**
      * @cfg {Object} defaultParamNames
      * Provides the default values for the {@link #paramNames} property. To globally modify the parameters
@@ -1278,13 +1363,17 @@ sortInfo: {
      * Destroys the store.
      */
     destroy : function(){
      * Destroys the store.
      */
     destroy : function(){
-        if(this.storeId){
-            Ext.StoreMgr.unregister(this);
+        if(!this.isDestroyed){
+            if(this.storeId){
+                Ext.StoreMgr.unregister(this);
+            }
+            this.clearData();
+            this.data = null;
+            Ext.destroy(this.proxy);
+            this.reader = this.writer = null;
+            this.purgeListeners();
+            this.isDestroyed = true;
         }
         }
-        this.data = null;
-        Ext.destroy(this.proxy);
-        this.reader = this.writer = null;
-        this.purgeListeners();
     },
 
     /**
     },
 
     /**
@@ -1327,6 +1416,7 @@ sortInfo: {
     remove : function(record){
         var index = this.data.indexOf(record);
         if(index > -1){
     remove : function(record){
         var index = this.data.indexOf(record);
         if(index > -1){
+            record.join(null);
             this.data.removeAt(index);
             if(this.pruneModifiedRecords){
                 this.modified.remove(record);
             this.data.removeAt(index);
             if(this.pruneModifiedRecords){
                 this.modified.remove(record);
@@ -1350,14 +1440,25 @@ sortInfo: {
      * Remove all Records from the Store and fires the {@link #clear} event.
      */
     removeAll : function(){
      * Remove all Records from the Store and fires the {@link #clear} event.
      */
     removeAll : function(){
-        this.data.clear();
+        var items = [];
+        this.each(function(rec){
+            items.push(rec);
+        });
+        this.clearData();
         if(this.snapshot){
             this.snapshot.clear();
         }
         if(this.pruneModifiedRecords){
             this.modified = [];
         }
         if(this.snapshot){
             this.snapshot.clear();
         }
         if(this.pruneModifiedRecords){
             this.modified = [];
         }
-        this.fireEvent('clear', this);
+        this.fireEvent('clear', this, items);
+    },
+
+    // private
+    onClear: function(store, records){
+        Ext.each(records, function(rec, index){
+            this.destroyRecord(this, rec, index);
+        }, this);
     },
 
     /**
     },
 
     /**
@@ -1429,6 +1530,14 @@ sortInfo: {
         this.lastOptions = o;
     },
 
         this.lastOptions = o;
     },
 
+    // private
+    clearData: function(){
+        this.data.each(function(rec) {
+            rec.join(null);
+        });
+        this.data.clear();
+    },
+
     /**
      * <p>Loads the Record cache from the configured <tt>{@link #proxy}</tt> using the configured <tt>{@link #reader}</tt>.</p>
      * <br><p>Notes:</p><div class="mdetail-params"><ul>
     /**
      * <p>Loads the Record cache from the configured <tt>{@link #proxy}</tt> using the configured <tt>{@link #reader}</tt>.</p>
      * <br><p>Notes:</p><div class="mdetail-params"><ul>
@@ -1560,10 +1669,12 @@ sortInfo: {
         var doRequest = true;
 
         if (action === 'read') {
         var doRequest = true;
 
         if (action === 'read') {
+            Ext.applyIf(options.params, this.baseParams);
             doRequest = this.fireEvent('beforeload', this, options);
         }
         else {
             doRequest = this.fireEvent('beforeload', this, options);
         }
         else {
-            // if Writer is configured as listful, force single-recoord rs to be [{}} instead of {}
+            // if Writer is configured as listful, force single-record rs to be [{}] instead of {}
+            // TODO Move listful rendering into DataWriter where the @cfg is defined.  Should be easy now.
             if (this.writer.listful === true && this.restful !== true) {
                 rs = (Ext.isArray(rs)) ? rs : [rs];
             }
             if (this.writer.listful === true && this.restful !== true) {
                 rs = (Ext.isArray(rs)) ? rs : [rs];
             }
@@ -1580,10 +1691,11 @@ sortInfo: {
             // Send request to proxy.
             var params = Ext.apply({}, options.params, this.baseParams);
             if (this.writer && this.proxy.url && !this.proxy.restful && !Ext.data.Api.hasUniqueUrl(this.proxy, action)) {
             // Send request to proxy.
             var params = Ext.apply({}, options.params, this.baseParams);
             if (this.writer && this.proxy.url && !this.proxy.restful && !Ext.data.Api.hasUniqueUrl(this.proxy, action)) {
-                params.xaction = action;
+                params.xaction = action;    // <-- really old, probaby unecessary.
             }
             }
-            // Note:  Up until this point we've been dealing with 'action' as a key from Ext.data.Api.actions.  We'll flip it now
-            // and send the value into DataProxy#request, since it's the value which maps to the DataProxy#api
+            // Note:  Up until this point we've been dealing with 'action' as a key from Ext.data.Api.actions.
+            // We'll flip it now and send the value into DataProxy#request, since it's the value which maps to
+            // the user's configured DataProxy#api
             this.proxy.request(Ext.data.Api.actions[action], rs, params, this.reader, this.createCallback(action, rs), this, options);
         }
         return doRequest;
             this.proxy.request(Ext.data.Api.actions[action], rs, params, this.reader, this.createCallback(action, rs), this, options);
         }
         return doRequest;
@@ -1666,7 +1778,7 @@ sortInfo: {
         var actions = Ext.data.Api.actions;
         return (action == 'read') ? this.loadRecords : function(data, response, success) {
             // calls: onCreateRecords | onUpdateRecords | onDestroyRecords
         var actions = Ext.data.Api.actions;
         return (action == 'read') ? this.loadRecords : function(data, response, success) {
             // calls: onCreateRecords | onUpdateRecords | onDestroyRecords
-            this['on' + Ext.util.Format.capitalize(action) + 'Records'](success, rs, data);
+            this['on' + Ext.util.Format.capitalize(action) + 'Records'](success, rs, [].concat(data));
             // If success === false here, exception will have been called in DataProxy
             if (success === true) {
                 this.fireEvent('write', this, action, data, response, rs);
             // If success === false here, exception will have been called in DataProxy
             if (success === true) {
                 this.fireEvent('write', this, action, data, response, rs);
@@ -1676,6 +1788,7 @@ sortInfo: {
 
     // Clears records from modified array after an exception event.
     // NOTE:  records are left marked dirty.  Do we want to commit them even though they were not updated/realized?
 
     // Clears records from modified array after an exception event.
     // NOTE:  records are left marked dirty.  Do we want to commit them even though they were not updated/realized?
+    // TODO remove this method?
     clearModified : function(rs) {
         if (Ext.isArray(rs)) {
             for (var n=rs.length-1;n>=0;n--) {
     clearModified : function(rs) {
         if (Ext.isArray(rs)) {
             for (var n=rs.length-1;n>=0;n--) {
@@ -1736,7 +1849,7 @@ sortInfo: {
     // @protected onDestroyRecords proxy callback for destroy action
     onDestroyRecords : function(success, rs, data) {
         // splice each rec out of this.removed
     // @protected onDestroyRecords proxy callback for destroy action
     onDestroyRecords : function(success, rs, data) {
         // splice each rec out of this.removed
-        rs = (rs instanceof Ext.data.Record) ? [rs] : rs;
+        rs = (rs instanceof Ext.data.Record) ? [rs] : [].concat(rs);
         for (var i=0,len=rs.length;i<len;i++) {
             this.removed.splice(this.removed.indexOf(rs[i]), 1);
         }
         for (var i=0,len=rs.length;i<len;i++) {
             this.removed.splice(this.removed.indexOf(rs[i]), 1);
         }
@@ -1791,7 +1904,7 @@ sortInfo: {
                 this.data = this.snapshot;
                 delete this.snapshot;
             }
                 this.data = this.snapshot;
                 delete this.snapshot;
             }
-            this.data.clear();
+            this.clearData();
             this.data.addAll(r);
             this.totalLength = t;
             this.applySort();
             this.data.addAll(r);
             this.totalLength = t;
             this.applySort();
@@ -2178,18 +2291,27 @@ sortInfo: {
         for(var i = 0, len = m.length; i < len; i++){
             m[i].reject();
         }
         for(var i = 0, len = m.length; i < len; i++){
             m[i].reject();
         }
+        var m = this.removed.slice(0).reverse();
+        this.removed = [];
+        for(var i = 0, len = m.length; i < len; i++){
+            this.insert(m[i].lastIndex||0, m[i]);
+            m[i].reject();
+        }
     },
 
     // private
     },
 
     // private
-    onMetaChange : function(meta, rtype, o){
-        this.recordType = rtype;
-        this.fields = rtype.prototype.fields;
+    onMetaChange : function(meta){
+        this.recordType = this.reader.recordType;
+        this.fields = this.recordType.prototype.fields;
         delete this.snapshot;
         delete this.snapshot;
-        if(meta.sortInfo){
-            this.sortInfo = meta.sortInfo;
+        if(this.reader.meta.sortInfo){
+            this.sortInfo = this.reader.meta.sortInfo;
         }else if(this.sortInfo  && !this.fields.get(this.sortInfo.field)){
             delete this.sortInfo;
         }
         }else if(this.sortInfo  && !this.fields.get(this.sortInfo.field)){
             delete this.sortInfo;
         }
+        if(this.writer){
+            this.writer.meta = this.reader.meta;
+        }
         this.modified = [];
         this.fireEvent('metachange', this, this.reader.meta);
     },
         this.modified = [];
         this.fireEvent('metachange', this, this.reader.meta);
     },
@@ -2507,14 +2629,49 @@ Ext.data.DataReader = function(meta, recordType){
      */\r
     this.recordType = Ext.isArray(recordType) ?\r
         Ext.data.Record.create(recordType) : recordType;\r
      */\r
     this.recordType = Ext.isArray(recordType) ?\r
         Ext.data.Record.create(recordType) : recordType;\r
+
+    // if recordType defined make sure extraction functions are defined\r
+    if (this.recordType){\r
+        this.buildExtractors();\r
+    }
 };\r
 \r
 Ext.data.DataReader.prototype = {\r
 };\r
 \r
 Ext.data.DataReader.prototype = {\r
-\r
     /**\r
     /**\r
-     * Abstract method, overridden in {@link Ext.data.JsonReader}\r
+     * @cfg {String} messageProperty [undefined] Optional name of a property within a server-response that represents a user-feedback message.\r
+     */\r
+    /**\r
+     * Abstract method created in extension's buildExtractors impl.\r
+     */\r
+    getTotal: Ext.emptyFn,\r
+    /**\r
+     * Abstract method created in extension's buildExtractors impl.\r
+     */\r
+    getRoot: Ext.emptyFn,\r
+    /**\r
+     * Abstract method created in extension's buildExtractors impl.\r
+     */\r
+    getMessage: Ext.emptyFn,\r
+    /**\r
+     * Abstract method created in extension's buildExtractors impl.\r
+     */\r
+    getSuccess: Ext.emptyFn,\r
+    /**\r
+     * Abstract method created in extension's buildExtractors impl.\r
+     */\r
+    getId: Ext.emptyFn,\r
+    /**\r
+     * Abstract method, overridden in DataReader extensions such as {@link Ext.data.JsonReader} and {@link Ext.data.XmlReader}\r
      */\r
     buildExtractors : Ext.emptyFn,\r
      */\r
     buildExtractors : Ext.emptyFn,\r
+    /**\r
+     * Abstract method overridden in DataReader extensions such as {@link Ext.data.JsonReader} and {@link Ext.data.XmlReader}\r
+     */\r
+    extractData : Ext.emptyFn,\r
+    /**\r
+     * Abstract method overridden in DataReader extensions such as {@link Ext.data.JsonReader} and {@link Ext.data.XmlReader}\r
+     */\r
+    extractValues : Ext.emptyFn,\r
 \r
     /**\r
      * Used for un-phantoming a record after a successful database insert.  Sets the records pk along with new data from server.\r
 \r
     /**\r
      * Used for un-phantoming a record after a successful database insert.  Sets the records pk along with new data from server.\r
@@ -2549,24 +2706,19 @@ Ext.data.DataReader.prototype = {
                 //rs.commit();\r
                 throw new Ext.data.DataReader.Error('realize', rs);\r
             }\r
                 //rs.commit();\r
                 throw new Ext.data.DataReader.Error('realize', rs);\r
             }\r
-            this.buildExtractors();\r
-            var values = this.extractValues(data, rs.fields.items, rs.fields.items.length);\r
             rs.phantom = false; // <-- That's what it's all about\r
             rs._phid = rs.id;  // <-- copy phantom-id -> _phid, so we can remap in Store#onCreateRecords\r
             rs.phantom = false; // <-- That's what it's all about\r
             rs._phid = rs.id;  // <-- copy phantom-id -> _phid, so we can remap in Store#onCreateRecords\r
-            rs.id = data[this.meta.idProperty];\r
-            rs.data = values;\r
+            rs.id = this.getId(data);\r
+            rs.data = data;\r
             rs.commit();\r
         }\r
     },\r
 \r
     /**\r
      * Used for updating a non-phantom or "real" record's data with fresh data from server after remote-save.\r
             rs.commit();\r
         }\r
     },\r
 \r
     /**\r
      * Used for updating a non-phantom or "real" record's data with fresh data from server after remote-save.\r
-     * You <b>must</b> return a complete new record from the server.  If you don't, your local record's missing fields\r
-     * will be populated with the default values specified in your Ext.data.Record.create specification.  Without a defaultValue,\r
-     * local fields will be populated with empty string "".  So return your entire record's data after both remote create and update.\r
-     * In addition, you <b>must</b> return record-data from the server in the same order received.\r
-     * Will perform a commit as well, un-marking dirty-fields.  Store's "update" event will be suppressed as the record receives\r
-     * a fresh new data-hash.\r
+     * If returning data from multiple-records after a batch-update, you <b>must</b> return record-data from the server in\r
+     * the same order received.  Will perform a commit as well, un-marking dirty-fields.  Store's "update" event will be\r
+     * suppressed as the record receives fresh new data-hash\r
      * @param {Record/Record[]} rs\r
      * @param {Object/Object[]} data\r
      */\r
      * @param {Record/Record[]} rs\r
      * @param {Object/Object[]} data\r
      */\r
@@ -2584,18 +2736,13 @@ Ext.data.DataReader.prototype = {
             }\r
         }\r
         else {\r
             }\r
         }\r
         else {\r
-                     // If rs is NOT an array but data IS, see if data contains just 1 record.  If so extract it and carry on.\r
+            // If rs is NOT an array but data IS, see if data contains just 1 record.  If so extract it and carry on.\r
             if (Ext.isArray(data) && data.length == 1) {\r
                 data = data.shift();\r
             }\r
             if (Ext.isArray(data) && data.length == 1) {\r
                 data = data.shift();\r
             }\r
-            if (!this.isData(data)) {\r
-                // TODO: create custom Exception class to return record in thrown exception.  Allow exception-handler the choice\r
-                // to commit or not rather than blindly rs.commit() here.\r
-                rs.commit();\r
-                throw new Ext.data.DataReader.Error('update', rs);\r
+            if (this.isData(data)) {\r
+                rs.data = Ext.apply(rs.data, data);\r
             }\r
             }\r
-            this.buildExtractors();\r
-            rs.data = this.extractValues(Ext.apply(rs.data, data), rs.fields.items, rs.fields.items.length);\r
             rs.commit();\r
         }\r
     },\r
             rs.commit();\r
         }\r
     },\r
@@ -2607,7 +2754,15 @@ Ext.data.DataReader.prototype = {
      * @return {Boolean}\r
      */\r
     isData : function(data) {\r
      * @return {Boolean}\r
      */\r
     isData : function(data) {\r
-        return (data && Ext.isObject(data) && !Ext.isEmpty(data[this.meta.idProperty])) ? true : false;\r
+        return (data && Ext.isObject(data) && !Ext.isEmpty(this.getId(data))) ? true : false;\r
+    },\r
+\r
+    // private function a store will createSequence upon\r
+    onMetaChange : function(meta){\r
+        delete this.ef;\r
+        this.meta = meta;\r
+        this.recordType = Ext.data.Record.create(meta.fields);\r
+        this.buildExtractors();\r
     }\r
 };\r
 \r
     }\r
 };\r
 \r
@@ -2632,6 +2787,40 @@ Ext.apply(Ext.data.DataReader.Error.prototype, {
 });\r
 \r
 \r
 });\r
 \r
 \r
+/**\r
+ * Ext.data.Response\r
+ * A generic response class to normalize response-handling internally to the framework.\r
+ * TODO move to own file, add to jsb.\r
+ */\r
+Ext.data.Response = function(params) {\r
+    Ext.apply(this, params);\r
+};\r
+Ext.data.Response.prototype = {\r
+    /**\r
+     * @property {String} action {@link Ext.data.Api#actions}\r
+     */\r
+    action: undefined,\r
+    /**\r
+     * @property {Boolean} success\r
+     */\r
+    success : undefined,\r
+    /**\r
+     * @property {String} message\r
+     */\r
+    message : undefined,\r
+    /**\r
+     * @property {Array/Object} data\r
+     */\r
+    data: undefined,\r
+    /**\r
+     * @property {Object} raw The raw response returned from server-code\r
+     */\r
+    raw: undefined,\r
+    /**\r
+     * @property {Ext.data.Record/Ext.data.Record[]} record(s) related to the Request action\r
+     */\r
+    records: undefined\r
+}\r
 /**
  * @class Ext.data.DataWriter
  * <p>Ext.data.DataWriter facilitates create, update, and destroy actions between
 /**
  * @class Ext.data.DataWriter
  * <p>Ext.data.DataWriter facilitates create, update, and destroy actions between
@@ -2678,14 +2867,8 @@ var store = new Ext.data.Store({
  * using {@link Ext.data.Record#create}.
  */
 Ext.data.DataWriter = function(config){
  * using {@link Ext.data.Record#create}.
  */
 Ext.data.DataWriter = function(config){
-    /**
-     * This DataWriter's configured metadata as passed to the constructor.
-     * @type Mixed
-     * @property meta
-     */
     Ext.apply(this, config);
 };
     Ext.apply(this, config);
 };
-
 Ext.data.DataWriter.prototype = {
 
     /**
 Ext.data.DataWriter.prototype = {
 
     /**
@@ -2710,7 +2893,18 @@ Ext.data.DataWriter.prototype = {
      * @param {Record/Record[]} rs The recordset write.
      */
     write : function(action, params, rs) {
      * @param {Record/Record[]} rs The recordset write.
      */
     write : function(action, params, rs) {
-        this.render(action, rs, params, this[action](rs));
+        var data    = [],
+        renderer    = action + 'Record';
+        // TODO implement @cfg listful here
+        if (Ext.isArray(rs)) {
+            Ext.each(rs, function(rec){
+                data.push(this[renderer](rec));
+            }, this);
+        }
+        else if (rs instanceof Ext.data.Record) {
+            data = this[renderer](rs);
+        }
+        this.render(action, rs, params, data);
     },
 
     /**
     },
 
     /**
@@ -2724,84 +2918,17 @@ Ext.data.DataWriter.prototype = {
     render : Ext.emptyFn,
 
     /**
     render : Ext.emptyFn,
 
     /**
-     * update
-     * @param {Object} p Params-hash to apply result to.
-     * @param {Record/Record[]} rs Record(s) to write
-     * @private
-     */
-    update : function(rs) {
-        var params = {};
-        if (Ext.isArray(rs)) {
-            var data = [],
-                ids = [];
-            Ext.each(rs, function(val){
-                ids.push(val.id);
-                data.push(this.updateRecord(val));
-            }, this);
-            params[this.meta.idProperty] = ids;
-            params[this.meta.root] = data;
-        }
-        else if (rs instanceof Ext.data.Record) {
-            params[this.meta.idProperty] = rs.id;
-            params[this.meta.root] = this.updateRecord(rs);
-        }
-        return params;
-    },
-
-    /**
-     * @cfg {Function} saveRecord Abstract method that should be implemented in all subclasses
-     * (e.g.: {@link Ext.data.JsonWriter#saveRecord JsonWriter.saveRecord}
+     * @cfg {Function} updateRecord Abstract method that should be implemented in all subclasses
+     * (e.g.: {@link Ext.data.JsonWriter#updateRecord JsonWriter.updateRecord}
      */
     updateRecord : Ext.emptyFn,
 
      */
     updateRecord : Ext.emptyFn,
 
-    /**
-     * create
-     * @param {Object} p Params-hash to apply result to.
-     * @param {Record/Record[]} rs Record(s) to write
-     * @private
-     */
-    create : function(rs) {
-        var params = {};
-        if (Ext.isArray(rs)) {
-            var data = [];
-            Ext.each(rs, function(val){
-                data.push(this.createRecord(val));
-            }, this);
-            params[this.meta.root] = data;
-        }
-        else if (rs instanceof Ext.data.Record) {
-            params[this.meta.root] = this.createRecord(rs);
-        }
-        return params;
-    },
-
     /**
      * @cfg {Function} createRecord Abstract method that should be implemented in all subclasses
      * (e.g.: {@link Ext.data.JsonWriter#createRecord JsonWriter.createRecord})
      */
     createRecord : Ext.emptyFn,
 
     /**
      * @cfg {Function} createRecord Abstract method that should be implemented in all subclasses
      * (e.g.: {@link Ext.data.JsonWriter#createRecord JsonWriter.createRecord})
      */
     createRecord : Ext.emptyFn,
 
-    /**
-     * destroy
-     * @param {Object} p Params-hash to apply result to.
-     * @param {Record/Record[]} rs Record(s) to write
-     * @private
-     */
-    destroy : function(rs) {
-        var params = {};
-        if (Ext.isArray(rs)) {
-            var data = [],
-                ids = [];
-            Ext.each(rs, function(val){
-                data.push(this.destroyRecord(val));
-            }, this);
-            params[this.meta.root] = data;
-        } else if (rs instanceof Ext.data.Record) {
-            params[this.meta.root] = this.destroyRecord(rs);
-        }
-        return params;
-    },
-
     /**
      * @cfg {Function} destroyRecord Abstract method that should be implemented in all subclasses
      * (e.g.: {@link Ext.data.JsonWriter#destroyRecord JsonWriter.destroyRecord})
     /**
      * @cfg {Function} destroyRecord Abstract method that should be implemented in all subclasses
      * (e.g.: {@link Ext.data.JsonWriter#destroyRecord JsonWriter.destroyRecord})
@@ -2809,7 +2936,7 @@ Ext.data.DataWriter.prototype = {
     destroyRecord : Ext.emptyFn,
 
     /**
     destroyRecord : Ext.emptyFn,
 
     /**
-     * Converts a Record to a hash
+     * Converts a Record to a hash.
      * @param {Record}
      * @private
      */
      * @param {Record}
      * @private
      */
@@ -2823,7 +2950,16 @@ Ext.data.DataWriter.prototype = {
                 data[m.mapping ? m.mapping : m.name] = value;
             }
         });
                 data[m.mapping ? m.mapping : m.name] = value;
             }
         });
-        data[this.meta.idProperty] = rec.id;
+        // we don't want to write Ext auto-generated id to hash.  Careful not to remove it on Models not having auto-increment pk though.
+        // We can tell its not auto-increment if the user defined a DataReader field for it *and* that field's value is non-empty.
+        // we could also do a RegExp here for the Ext.data.Record AUTO_ID prefix.
+        if (rec.phantom) {
+            if (rec.fields.containsKey(this.meta.idProperty) && Ext.isEmpty(rec.data[this.meta.idProperty])) {
+                delete data[this.meta.idProperty];
+            }
+        } else {
+            data[this.meta.idProperty] = rec.id
+        }
         return data;
     }
 };/**\r
         return data;
     }
 };/**\r
@@ -2889,7 +3025,27 @@ api: {
     update  : undefined,\r
     destroy : undefined\r
 }\r
     update  : undefined,\r
     destroy : undefined\r
 }\r
-</code></pre>\r
+     * </code></pre>\r
+     * <p>The url is built based upon the action being executed <tt>[load|create|save|destroy]</tt>\r
+     * using the commensurate <tt>{@link #api}</tt> property, or if undefined default to the\r
+     * configured {@link Ext.data.Store}.{@link Ext.data.Store#url url}.</p><br>\r
+     * <p>For example:</p>\r
+     * <pre><code>\r
+api: {\r
+    load :    '/controller/load',\r
+    create :  '/controller/new',  // Server MUST return idProperty of new record\r
+    save :    '/controller/update',\r
+    destroy : '/controller/destroy_action'\r
+}\r
+\r
+// Alternatively, one can use the object-form to specify each API-action\r
+api: {\r
+    load: {url: 'read.php', method: 'GET'},\r
+    create: 'create.php',\r
+    destroy: 'destroy.php',\r
+    save: 'update.php'\r
+}\r
+     * </code></pre>\r
      * <p>If the specific URL for a given CRUD action is undefined, the CRUD action request\r
      * will be directed to the configured <tt>{@link Ext.data.Connection#url url}</tt>.</p>\r
      * <br><p><b>Note</b>: To modify the URL for an action dynamically the appropriate API\r
      * <p>If the specific URL for a given CRUD action is undefined, the CRUD action request\r
      * will be directed to the configured <tt>{@link Ext.data.Connection#url url}</tt>.</p>\r
      * <br><p><b>Note</b>: To modify the URL for an action dynamically the appropriate API\r
@@ -2907,22 +3063,9 @@ myStore.on({
             // permanent, applying this URL for all subsequent requests.\r
             store.proxy.setUrl('changed1.php', true);\r
 \r
             // permanent, applying this URL for all subsequent requests.\r
             store.proxy.setUrl('changed1.php', true);\r
 \r
-            // manually set the <b>private</b> connection URL.\r
-            // <b>Warning:</b>  Accessing the private URL property should be avoided.\r
-            // Use the public method <tt>{@link Ext.data.HttpProxy#setUrl setUrl}</tt> instead, shown above.\r
-            // It should be noted that changing the URL like this will affect\r
-            // the URL for just this request.  Subsequent requests will use the\r
-            // API or URL defined in your initial proxy configuration.\r
-            store.proxy.conn.url = 'changed1.php';\r
-\r
-            // proxy URL will be superseded by API (only if proxy created to use ajax):\r
-            // It should be noted that proxy API changes are permanent and will\r
-            // be used for all subsequent requests.\r
-            store.proxy.api.load = 'changed2.php';\r
-\r
-            // However, altering the proxy API should be done using the public\r
-            // method <tt>{@link Ext.data.DataProxy#setApi setApi}</tt> instead.\r
-            store.proxy.setApi('load', 'changed2.php');\r
+            // Altering the proxy API should be done using the public\r
+            // method <tt>{@link Ext.data.DataProxy#setApi setApi}</tt>.\r
+            store.proxy.setApi('read', 'changed2.php');\r
 \r
             // Or set the entire API with a config-object.\r
             // When using the config-object option, you must redefine the <b>entire</b>\r
 \r
             // Or set the entire API with a config-object.\r
             // When using the config-object option, you must redefine the <b>entire</b>\r
@@ -2939,23 +3082,17 @@ myStore.on({
      * </code></pre>\r
      * </p>\r
      */\r
      * </code></pre>\r
      * </p>\r
      */\r
-    // Prepare the proxy api.  Ensures all API-actions are defined with the Object-form.\r
-    try {\r
-        Ext.data.Api.prepare(this);\r
-    } catch (e) {\r
-        if (e instanceof Ext.data.Api.Error) {\r
-            e.toConsole();\r
-        }\r
-    }\r
 \r
     this.addEvents(\r
         /**\r
          * @event exception\r
 \r
     this.addEvents(\r
         /**\r
          * @event exception\r
-         * <p>Fires if an exception occurs in the Proxy during a remote request.\r
-         * This event is relayed through a corresponding\r
-         * {@link Ext.data.Store}.{@link Ext.data.Store#exception exception},\r
-         * so any Store instance may observe this event.\r
-         * This event can be fired for one of two reasons:</p>\r
+         * <p>Fires if an exception occurs in the Proxy during a remote request. This event is relayed\r
+         * through a corresponding {@link Ext.data.Store}.{@link Ext.data.Store#exception exception},\r
+         * so any Store instance may observe this event.</p>\r
+         * <p>In addition to being fired through the DataProxy instance that raised the event, this event is also fired\r
+         * through the Ext.data.DataProxy <i>class</i> to allow for centralized processing of exception events from <b>all</b>\r
+         * DataProxies by attaching a listener to the Ext.data.Proxy class itself.</p>\r
+         * <p>This event can be fired for one of two reasons:</p>\r
          * <div class="mdetail-params"><ul>\r
          * <li>remote-request <b>failed</b> : <div class="sub-desc">\r
          * The server did not return status === 200.\r
          * <div class="mdetail-params"><ul>\r
          * <li>remote-request <b>failed</b> : <div class="sub-desc">\r
          * The server did not return status === 200.\r
@@ -3039,7 +3176,10 @@ myStore.on({
         'loadexception',\r
         /**\r
          * @event beforewrite\r
         'loadexception',\r
         /**\r
          * @event beforewrite\r
-         * Fires before a request is generated for one of the actions Ext.data.Api.actions.create|update|destroy\r
+         * <p>Fires before a request is generated for one of the actions Ext.data.Api.actions.create|update|destroy</p>\r
+         * <p>In addition to being fired through the DataProxy instance that raised the event, this event is also fired\r
+         * through the Ext.data.DataProxy <i>class</i> to allow for centralized processing of beforewrite events from <b>all</b>\r
+         * DataProxies by attaching a listener to the Ext.data.Proxy class itself.</p>\r
          * @param {DataProxy} this The proxy for the request\r
          * @param {String} action [Ext.data.Api.actions.create|update|destroy]\r
          * @param {Record/Array[Record]} rs The Record(s) to create|update|destroy.\r
          * @param {DataProxy} this The proxy for the request\r
          * @param {String} action [Ext.data.Api.actions.create|update|destroy]\r
          * @param {Record/Array[Record]} rs The Record(s) to create|update|destroy.\r
@@ -3048,7 +3188,10 @@ myStore.on({
         'beforewrite',\r
         /**\r
          * @event write\r
         'beforewrite',\r
         /**\r
          * @event write\r
-         * Fires before the request-callback is called\r
+         * <p>Fires before the request-callback is called</p>\r
+         * <p>In addition to being fired through the DataProxy instance that raised the event, this event is also fired\r
+         * through the Ext.data.DataProxy <i>class</i> to allow for centralized processing of write events from <b>all</b>\r
+         * DataProxies by attaching a listener to the Ext.data.Proxy class itself.</p>\r
          * @param {DataProxy} this The proxy that sent the request\r
          * @param {String} action [Ext.data.Api.actions.create|upate|destroy]\r
          * @param {Object} data The data object extracted from the server-response\r
          * @param {DataProxy} this The proxy that sent the request\r
          * @param {String} action [Ext.data.Api.actions.create|upate|destroy]\r
          * @param {Object} data The data object extracted from the server-response\r
@@ -3059,6 +3202,17 @@ myStore.on({
         'write'\r
     );\r
     Ext.data.DataProxy.superclass.constructor.call(this);\r
         'write'\r
     );\r
     Ext.data.DataProxy.superclass.constructor.call(this);\r
+\r
+    // Prepare the proxy api.  Ensures all API-actions are defined with the Object-form.\r
+    try {\r
+        Ext.data.Api.prepare(this);\r
+    } catch (e) {\r
+        if (e instanceof Ext.data.Api.Error) {\r
+            e.toConsole();\r
+        }\r
+    }\r
+    // relay each proxy's events onto Ext.data.DataProxy class for centralized Proxy-listening\r
+    Ext.data.DataProxy.relayEvents(this, ['beforewrite', 'write', 'exception']);\r
 };\r
 \r
 Ext.extend(Ext.data.DataProxy, Ext.util.Observable, {\r
 };\r
 \r
 Ext.extend(Ext.data.DataProxy, Ext.util.Observable, {\r
@@ -3077,7 +3231,7 @@ store: new Ext.data.Store({
     ...\r
 )}\r
      * </code></pre>\r
     ...\r
 )}\r
      * </code></pre>\r
-     * There is no <code>{@link #api}</code> specified in the configuration of the proxy,\r
+     * If there is no <code>{@link #api}</code> specified in the configuration of the proxy,\r
      * all requests will be marshalled to a single RESTful url (/users) so the serverside\r
      * framework can inspect the HTTP Method and act accordingly:\r
      * <pre>\r
      * all requests will be marshalled to a single RESTful url (/users) so the serverside\r
      * framework can inspect the HTTP Method and act accordingly:\r
      * <pre>\r
@@ -3087,6 +3241,18 @@ GET      /users     read
 PUT      /users/23  update\r
 DESTROY  /users/23  delete\r
      * </pre></p>\r
 PUT      /users/23  update\r
 DESTROY  /users/23  delete\r
      * </pre></p>\r
+     * <p>If set to <tt>true</tt>, a {@link Ext.data.Record#phantom non-phantom} record's\r
+     * {@link Ext.data.Record#id id} will be appended to the url. Some MVC (e.g., Ruby on Rails,\r
+     * Merb and Django) support segment based urls where the segments in the URL follow the\r
+     * Model-View-Controller approach:<pre><code>\r
+     * someSite.com/controller/action/id\r
+     * </code></pre>\r
+     * Where the segments in the url are typically:<div class="mdetail-params"><ul>\r
+     * <li>The first segment : represents the controller class that should be invoked.</li>\r
+     * <li>The second segment : represents the class function, or method, that should be called.</li>\r
+     * <li>The third segment : represents the ID (a variable typically passed to the method).</li>\r
+     * </ul></div></p>\r
+     * <br><p>Refer to <code>{@link Ext.data.DataProxy#api}</code> for additional information.</p>\r
      */\r
     restful: false,\r
 \r
      */\r
     restful: false,\r
 \r
@@ -3202,11 +3368,16 @@ proxy.setApi(Ext.data.Api.actions.read, '/users/new_load_url');
             throw new Ext.data.Api.Error('invalid-url', action);\r
         }\r
 \r
             throw new Ext.data.Api.Error('invalid-url', action);\r
         }\r
 \r
+        // look for urls having "provides" suffix (from Rails/Merb and others...),\r
+        // e.g.: /users.json, /users.xml, etc\r
+        // with restful routes, we need urls like:\r
+        // PUT /users/1.json\r
+        // DELETE /users/1.json\r
         var format = null;\r
         var format = null;\r
-        var m = url.match(/(.*)(\.\w+)$/);  // <-- look for urls with "provides" suffix, e.g.: /users.json, /users.xml, etc\r
+        var m = url.match(/(.*)(\.json|\.xml|\.html)$/);\r
         if (m) {\r
         if (m) {\r
-            format = m[2];\r
-            url = m[1];\r
+            format = m[2];  // eg ".json"\r
+            url = m[1];     // eg: "/users"\r
         }\r
         // prettyUrls is deprectated in favor of restful-config\r
         if ((this.prettyUrls === true || this.restful === true) && record instanceof Ext.data.Record && !record.phantom) {\r
         }\r
         // prettyUrls is deprectated in favor of restful-config\r
         if ((this.prettyUrls === true || this.restful === true) && record instanceof Ext.data.Record && !record.phantom) {\r
@@ -3226,6 +3397,11 @@ proxy.setApi(Ext.data.Api.actions.read, '/users/new_load_url');
     }\r
 });\r
 \r
     }\r
 });\r
 \r
+// Apply the Observable prototype to the DataProxy class so that proxy instances can relay their\r
+// events to the class.  Allows for centralized listening of all proxy instances upon the DataProxy class.\r
+Ext.apply(Ext.data.DataProxy, Ext.util.Observable.prototype);\r
+Ext.util.Observable.call(Ext.data.DataProxy);\r
+\r
 /**\r
  * @class Ext.data.DataProxy.Error\r
  * @extends Ext.Error\r
 /**\r
  * @class Ext.data.DataProxy.Error\r
  * @extends Ext.Error\r
@@ -3247,6 +3423,8 @@ Ext.apply(Ext.data.DataProxy.Error.prototype, {
         'api-invalid': 'Recieved an invalid API-configuration.  Please ensure your proxy API-configuration contains only the actions from Ext.data.Api.actions.'\r
     }\r
 });\r
         'api-invalid': 'Recieved an invalid API-configuration.  Please ensure your proxy API-configuration contains only the actions from Ext.data.Api.actions.'\r
     }\r
 });\r
+\r
+\r
 /**\r
  * @class Ext.data.ScriptTagProxy\r
  * @extends Ext.data.DataProxy\r
 /**\r
  * @class Ext.data.ScriptTagProxy\r
  * @extends Ext.data.DataProxy\r
@@ -3445,23 +3623,23 @@ Ext.extend(Ext.data.ScriptTagProxy, Ext.data.DataProxy, {
      * @param {Object} res The server response\r
      * @private\r
      */\r
      * @param {Object} res The server response\r
      * @private\r
      */\r
-    onWrite : function(action, trans, res, rs) {\r
+    onWrite : function(action, trans, response, rs) {\r
         var reader = trans.reader;\r
         try {\r
             // though we already have a response object here in STP, run through readResponse to catch any meta-data exceptions.\r
         var reader = trans.reader;\r
         try {\r
             // though we already have a response object here in STP, run through readResponse to catch any meta-data exceptions.\r
-            reader.readResponse(action, res);\r
+            var res = reader.readResponse(action, response);\r
         } catch (e) {\r
             this.fireEvent('exception', this, 'response', action, trans, res, e);\r
             trans.callback.call(trans.scope||window, null, res, false);\r
             return;\r
         }\r
         } catch (e) {\r
             this.fireEvent('exception', this, 'response', action, trans, res, e);\r
             trans.callback.call(trans.scope||window, null, res, false);\r
             return;\r
         }\r
-        if(!res[reader.meta.successProperty] === true){\r
+        if(!res.success === true){\r
             this.fireEvent('exception', this, 'remote', action, trans, res, rs);\r
             trans.callback.call(trans.scope||window, null, res, false);\r
             return;\r
         }\r
             this.fireEvent('exception', this, 'remote', action, trans, res, rs);\r
             trans.callback.call(trans.scope||window, null, res, false);\r
             return;\r
         }\r
-        this.fireEvent("write", this, action, res[reader.meta.root], res, rs, trans.arg );\r
-        trans.callback.call(trans.scope||window, res[reader.meta.root], res, true);\r
+        this.fireEvent("write", this, action, res.data, res, rs, trans.arg );\r
+        trans.callback.call(trans.scope||window, res.data, res, true);\r
     },\r
 \r
     // private\r
     },\r
 \r
     // private\r
@@ -3553,13 +3731,13 @@ Ext.data.HttpProxy = function(conn){
 \r
     // nullify the connection url.  The url param has been copied to 'this' above.  The connection\r
     // url will be set during each execution of doRequest when buildUrl is called.  This makes it easier for users to override the\r
 \r
     // nullify the connection url.  The url param has been copied to 'this' above.  The connection\r
     // url will be set during each execution of doRequest when buildUrl is called.  This makes it easier for users to override the\r
-    // connection url during beforeaction events (ie: beforeload, beforesave, etc).  The connection url will be nullified\r
-    // after each request as well.  Url is always re-defined during doRequest.\r
+    // connection url during beforeaction events (ie: beforeload, beforewrite, etc).\r
+    // Url is always re-defined during doRequest.\r
     this.conn.url = null;\r
 \r
     this.useAjax = !conn || !conn.events;\r
 \r
     this.conn.url = null;\r
 \r
     this.useAjax = !conn || !conn.events;\r
 \r
-    //private.  A hash containing active requests, keyed on action [Ext.data.Api.actions.create|read|update|destroy]\r
+    // A hash containing active requests, keyed on action [Ext.data.Api.actions.create|read|update|destroy]\r
     var actions = Ext.data.Api.actions;\r
     this.activeRequest = {};\r
     for (var verb in actions) {\r
     var actions = Ext.data.Api.actions;\r
     this.activeRequest = {};\r
     for (var verb in actions) {\r
@@ -3568,40 +3746,6 @@ Ext.data.HttpProxy = function(conn){
 };\r
 \r
 Ext.extend(Ext.data.HttpProxy, Ext.data.DataProxy, {\r
 };\r
 \r
 Ext.extend(Ext.data.HttpProxy, Ext.data.DataProxy, {\r
-    /**\r
-     * @cfg {Boolean} restful\r
-     * <p>If set to <tt>true</tt>, a {@link Ext.data.Record#phantom non-phantom} record's\r
-     * {@link Ext.data.Record#id id} will be appended to the url (defaults to <tt>false</tt>).</p><br>\r
-     * <p>The url is built based upon the action being executed <tt>[load|create|save|destroy]</tt>\r
-     * using the commensurate <tt>{@link #api}</tt> property, or if undefined default to the\r
-     * configured {@link Ext.data.Store}.{@link Ext.data.Store#url url}.</p><br>\r
-     * <p>Some MVC (e.g., Ruby on Rails, Merb and Django) support this style of segment based urls\r
-     * where the segments in the URL follow the Model-View-Controller approach.</p><pre><code>\r
-     * someSite.com/controller/action/id\r
-     * </code></pre>\r
-     * Where the segments in the url are typically:<div class="mdetail-params"><ul>\r
-     * <li>The first segment : represents the controller class that should be invoked.</li>\r
-     * <li>The second segment : represents the class function, or method, that should be called.</li>\r
-     * <li>The third segment : represents the ID (a variable typically passed to the method).</li>\r
-     * </ul></div></p>\r
-     * <p>For example:</p>\r
-     * <pre><code>\r
-api: {\r
-    load :    '/controller/load',\r
-    create :  '/controller/new',  // Server MUST return idProperty of new record\r
-    save :    '/controller/update',\r
-    destroy : '/controller/destroy_action'\r
-}\r
-\r
-// Alternatively, one can use the object-form to specify each API-action\r
-api: {\r
-    load: {url: 'read.php', method: 'GET'},\r
-    create: 'create.php',\r
-    destroy: 'destroy.php',\r
-    save: 'update.php'\r
-}\r
-     */\r
-\r
     /**\r
      * Return the {@link Ext.data.Connection} object being used by this Proxy.\r
      * @return {Connection} The Connection object. This object may be used to subscribe to events on\r
     /**\r
      * Return the {@link Ext.data.Connection} object being used by this Proxy.\r
      * @return {Connection} The Connection object. This object may be used to subscribe to events on\r
@@ -3625,6 +3769,7 @@ api: {
         this.conn.url = url;\r
         if (makePermanent === true) {\r
             this.url = url;\r
         this.conn.url = url;\r
         if (makePermanent === true) {\r
             this.url = url;\r
+            this.api = null;\r
             Ext.data.Api.prepare(this);\r
         }\r
     },\r
             Ext.data.Api.prepare(this);\r
         }\r
     },\r
@@ -3658,10 +3803,14 @@ api: {
             callback : this.createCallback(action, rs),\r
             scope: this\r
         };\r
             callback : this.createCallback(action, rs),\r
             scope: this\r
         };\r
-        // Sample the request data:  If it's an object, then it hasn't been json-encoded yet.\r
-        // Transmit data using jsonData config of Ext.Ajax.request\r
-        if (typeof(params[reader.meta.root]) === 'object') {\r
-            o.jsonData = params;\r
+\r
+        // If possible, transmit data using jsonData || xmlData on Ext.Ajax.request (An installed DataWriter would have written it there.).\r
+        // Use std HTTP params otherwise.\r
+        // TODO wrap into 1 Ext.apply now?\r
+        if (params.jsonData) {\r
+            o.jsonData = params.jsonData;\r
+        } else if (params.xmlData) {\r
+            o.xmlData = params.xmlData;\r
         } else {\r
             o.params = params || {};\r
         }\r
         } else {\r
             o.params = params || {};\r
         }\r
@@ -3671,7 +3820,7 @@ api: {
         if (this.conn.url === null) {\r
             this.conn.url = this.buildUrl(action, rs);\r
         }\r
         if (this.conn.url === null) {\r
             this.conn.url = this.buildUrl(action, rs);\r
         }\r
-        else if (this.restful === true && rs instanceof Ext.data.Record && !rs.phantom) {\r
+        else if (this.restful === true && rs instanceof Ext.data.Record && !rs.phantom) { // <-- user must have intervened with #setApi or #setUrl\r
             this.conn.url += '/' + rs.id;\r
         }\r
         if(this.useAjax){\r
             this.conn.url += '/' + rs.id;\r
         }\r
         if(this.useAjax){\r
@@ -3680,7 +3829,10 @@ api: {
 \r
             // If a currently running request is found for this action, abort it.\r
             if (this.activeRequest[action]) {\r
 \r
             // If a currently running request is found for this action, abort it.\r
             if (this.activeRequest[action]) {\r
+                ////\r
                 // Disabled aborting activeRequest while implementing REST.  activeRequest[action] will have to become an array\r
                 // Disabled aborting activeRequest while implementing REST.  activeRequest[action] will have to become an array\r
+                // TODO ideas anyone?\r
+                //\r
                 //Ext.Ajax.abort(this.activeRequest[action]);\r
             }\r
             this.activeRequest[action] = Ext.Ajax.request(o);\r
                 //Ext.Ajax.abort(this.activeRequest[action]);\r
             }\r
             this.activeRequest[action] = Ext.Ajax.request(o);\r
@@ -3704,6 +3856,7 @@ api: {
             if (!success) {\r
                 if (action === Ext.data.Api.actions.read) {\r
                     // @deprecated: fire loadexception for backwards compat.\r
             if (!success) {\r
                 if (action === Ext.data.Api.actions.read) {\r
                     // @deprecated: fire loadexception for backwards compat.\r
+                    // TODO remove in 3.1\r
                     this.fireEvent('loadexception', this, o, response);\r
                 }\r
                 this.fireEvent('exception', this, 'response', action, o, response);\r
                     this.fireEvent('loadexception', this, o, response);\r
                 }\r
                 this.fireEvent('exception', this, 'response', action, o, response);\r
@@ -3723,6 +3876,9 @@ api: {
      * @param {String} action Action name as per {@link Ext.data.Api.actions#read}.\r
      * @param {Object} o The request transaction object\r
      * @param {Object} res The server response\r
      * @param {String} action Action name as per {@link Ext.data.Api.actions#read}.\r
      * @param {Object} o The request transaction object\r
      * @param {Object} res The server response\r
+     * @fires loadexception (deprecated)\r
+     * @fires exception\r
+     * @fires load\r
      * @private\r
      */\r
     onRead : function(action, o, response) {\r
      * @private\r
      */\r
     onRead : function(action, o, response) {\r
@@ -3731,13 +3887,16 @@ api: {
             result = o.reader.read(response);\r
         }catch(e){\r
             // @deprecated: fire old loadexception for backwards-compat.\r
             result = o.reader.read(response);\r
         }catch(e){\r
             // @deprecated: fire old loadexception for backwards-compat.\r
+            // TODO remove in 3.1\r
             this.fireEvent('loadexception', this, o, response, e);\r
             this.fireEvent('loadexception', this, o, response, e);\r
+\r
             this.fireEvent('exception', this, 'response', action, o, response, e);\r
             o.request.callback.call(o.request.scope, null, o.request.arg, false);\r
             return;\r
         }\r
         if (result.success === false) {\r
             // @deprecated: fire old loadexception for backwards-compat.\r
             this.fireEvent('exception', this, 'response', action, o, response, e);\r
             o.request.callback.call(o.request.scope, null, o.request.arg, false);\r
             return;\r
         }\r
         if (result.success === false) {\r
             // @deprecated: fire old loadexception for backwards-compat.\r
+            // TODO remove in 3.1\r
             this.fireEvent('loadexception', this, o, response);\r
 \r
             // Get DataReader read-back a response-object to pass along to exception event\r
             this.fireEvent('loadexception', this, o, response);\r
 \r
             // Get DataReader read-back a response-object to pass along to exception event\r
@@ -3747,6 +3906,9 @@ api: {
         else {\r
             this.fireEvent('load', this, o, o.request.arg);\r
         }\r
         else {\r
             this.fireEvent('load', this, o, o.request.arg);\r
         }\r
+        // TODO refactor onRead, onWrite to be more generalized now that we're dealing with Ext.data.Response instance\r
+        // the calls to request.callback(...) in each will have to be made identical.\r
+        // NOTE reader.readResponse does not currently return Ext.data.Response\r
         o.request.callback.call(o.request.scope, result, o.request.arg, result.success);\r
     },\r
     /**\r
         o.request.callback.call(o.request.scope, result, o.request.arg, result.success);\r
     },\r
     /**\r
@@ -3754,6 +3916,8 @@ api: {
      * @param {String} action [Ext.data.Api.actions.create|read|update|destroy]\r
      * @param {Object} trans The request transaction object\r
      * @param {Object} res The server response\r
      * @param {String} action [Ext.data.Api.actions.create|read|update|destroy]\r
      * @param {Object} trans The request transaction object\r
      * @param {Object} res The server response\r
+     * @fires exception\r
+     * @fires write\r
      * @private\r
      */\r
     onWrite : function(action, o, response, rs) {\r
      * @private\r
      */\r
     onWrite : function(action, o, response, rs) {\r
@@ -3766,12 +3930,15 @@ api: {
             o.request.callback.call(o.request.scope, null, o.request.arg, false);\r
             return;\r
         }\r
             o.request.callback.call(o.request.scope, null, o.request.arg, false);\r
             return;\r
         }\r
-        if (res[reader.meta.successProperty] === false) {\r
+        if (res.success === false) {\r
             this.fireEvent('exception', this, 'remote', action, o, res, rs);\r
         } else {\r
             this.fireEvent('exception', this, 'remote', action, o, res, rs);\r
         } else {\r
-            this.fireEvent('write', this, action, res[reader.meta.root], res, rs, o.request.arg);\r
+            this.fireEvent('write', this, action, res.data, res, rs, o.request.arg);\r
         }\r
         }\r
-        o.request.callback.call(o.request.scope, res[reader.meta.root], res, res[reader.meta.successProperty]);\r
+        // TODO refactor onRead, onWrite to be more generalized now that we're dealing with Ext.data.Response instance\r
+        // the calls to request.callback(...) in each will have to be made similar.\r
+        // NOTE reader.readResponse does not currently return Ext.data.Response\r
+        o.request.callback.call(o.request.scope, res.data, res, res.success);\r
     },\r
 \r
     // inherit docs\r
     },\r
 \r
     // inherit docs\r