docs: function(features, options) {
          options = OpenLayers.Util.applyDefaults(options, this.options);
          var resp = [], nResponses = 0;
          
          // Divide up features before issuing any requests.  This properly
          // counts requests in the event that any responses come in before
          // all requests have been issued.
          var types = {};
          types[OpenLayers.State.INSERT] = [];
          types[OpenLayers.State.UPDATE] = [];
          types[OpenLayers.State.DELETE] = [];
          var feature, list, requestFeatures = [];
          for(var i=0, len=features.length; i<len; ++i) {
              feature = features[i];
              list = types[feature.state];
              if(list) {
                  list.push(feature);
                  requestFeatures.push(feature); 
              }
          }
          // tally up number of requests
          var nRequests = (types[OpenLayers.State.INSERT].length > 0 ? 1 : 0) +
              types[OpenLayers.State.UPDATE].length +
              types[OpenLayers.State.DELETE].length;
          
          // This response will be sent to the final callback after all the others
          // have been fired.
          var success = true;
          var finalResponse = new OpenLayers.Protocol.Response({
              reqFeatures: requestFeatures        
          });
          
          function insertCallback(response) {
              var len = response.features ? response.features.length : 0;
              var fids = new Array(len);
              for(var i=0; i<len; ++i) {
                  fids[i] = response.features[i].fid;
              }   
              finalResponse.insertIds = fids;
              callback.apply(this, [response]);
          }
   
          function callback(response) {
              this.callUserCallback(response, options);
              success = success && response.success();
              nResponses++;
              if (nResponses >= nRequests) {
                  if (options.callback) {
                      finalResponse.code = success ? 
                          OpenLayers.Protocol.Response.SUCCESS :
                          OpenLayers.Protocol.Response.FAILURE;
                      options.callback.apply(options.scope, [finalResponse]);
                  }    
              }
          }
  
          // start issuing requests
          var queue = types[OpenLayers.State.INSERT];
          if(queue.length > 0) {
              resp.push(this.create(
                  queue, OpenLayers.Util.applyDefaults(
                      {callback: insertCallback, scope: this}, options.create
                  )
              ));
          }
          queue = types[OpenLayers.State.UPDATE];
          for(var i=queue.length-1; i>=0; --i) {
              resp.push(this.update(
                  queue[i], OpenLayers.Util.applyDefaults(
                      {callback: callback, scope: this}, options.update
                  ))
              );
          }
          queue = types[OpenLayers.State.DELETE];
          for(var i=queue.length-1; i>=0; --i) {
              resp.push(this["delete"](
                  queue[i], OpenLayers.Util.applyDefaults(
                      {callback: callback, scope: this}, options["delete"]
                  ))
              );
          }
          return resp;
      }