web-apps/vendor/touch/src/data/Operation.js
Maxim Kadushkin 741b10515d webapps added
2016-03-10 21:48:53 -03:00

467 lines
14 KiB
JavaScript

/**
* @author Ed Spencer
*
* Represents a single read or write operation performed by a {@link Ext.data.proxy.Proxy Proxy}. Operation objects are
* used to enable communication between Stores and Proxies. Application developers should rarely need to interact with
* Operation objects directly.
*
* Note that when you define an Operation directly, you need to specify at least the {@link #model} configuration.
*
* Several Operations can be batched together in a {@link Ext.data.Batch batch}.
*/
Ext.define('Ext.data.Operation', {
config: {
/**
* @cfg {Boolean} synchronous
* True if this Operation is to be executed synchronously. This property is inspected by a
* {@link Ext.data.Batch Batch} to see if a series of Operations can be executed in parallel or not.
* @accessor
*/
synchronous: true,
/**
* @cfg {String} action
* The action being performed by this Operation. Should be one of 'create', 'read', 'update' or 'destroy'.
* @accessor
*/
action: null,
/**
* @cfg {Ext.util.Filter[]} filters
* Optional array of filter objects. Only applies to 'read' actions.
* @accessor
*/
filters: null,
/**
* @cfg {Ext.util.Sorter[]} sorters
* Optional array of sorter objects. Only applies to 'read' actions.
* @accessor
*/
sorters: null,
/**
* @cfg {Ext.util.Grouper} grouper
* Optional grouping configuration. Only applies to 'read' actions where grouping is desired.
* @accessor
*/
grouper: null,
/**
* @cfg {Number} start
* The start index (offset), used in paging when running a 'read' action.
* @accessor
*/
start: null,
/**
* @cfg {Number} limit
* The number of records to load. Used on 'read' actions when paging is being used.
* @accessor
*/
limit: null,
/**
* @cfg {Ext.data.Batch} batch
* The batch that this Operation is a part of.
* @accessor
*/
batch: null,
/**
* @cfg {Function} callback
* Function to execute when operation completed.
* @cfg {Ext.data.Model[]} callback.records Array of records.
* @cfg {Ext.data.Operation} callback.operation The Operation itself.
* @accessor
*/
callback: null,
/**
* @cfg {Object} scope
* Scope for the {@link #callback} function.
* @accessor
*/
scope: null,
/**
* @cfg {Ext.data.ResultSet} resultSet
* The ResultSet for this operation.
* @accessor
*/
resultSet: null,
/**
* @cfg {Array} records
* The records associated to this operation. Before an operation starts, these
* are the records you are updating, creating, or destroying. After an operation
* is completed, a Proxy usually sets these records on the Operation to become
* the processed records. If you don't set these records on your operation in
* your proxy, then the getter will return the ones defined on the {@link #resultSet}
* @accessor
*/
records: null,
/**
* @cfg {Ext.data.Request} request
* The request used for this Operation. Operations don't usually care about Request and Response data, but in the
* ServerProxy and any of its subclasses they may be useful for further processing.
* @accessor
*/
request: null,
/**
* @cfg {Object} response
* The response that was gotten from the server if there was one.
* @accessor
*/
response: null,
/**
* @cfg {Boolean} withCredentials
* This field is necessary when using cross-origin resource sharing.
* @accessor
*/
withCredentials: null,
/**
* @cfg {Object} params
* The params send along with this operation. These usually apply to a Server proxy if you are
* creating your own custom proxy,
* @accessor
*/
params: null,
url: null,
page: null,
node: null,
/**
* @cfg {Ext.data.Model} model
* The Model that this Operation will be dealing with. This configuration is required when defining any Operation.
* Since Operations take care of creating, updating, destroying and reading records, it needs access to the Model.
* @accessor
*/
model: undefined,
addRecords: false
},
/**
* @property {Boolean} started
* Property tracking the start status of this Operation. Use {@link #isStarted}.
* @private
* @readonly
*/
started: false,
/**
* @property {Boolean} running
* Property tracking the run status of this Operation. Use {@link #isRunning}.
* @private
* @readonly
*/
running: false,
/**
* @property {Boolean} complete
* Property tracking the completion status of this Operation. Use {@link #isComplete}.
* @private
* @readonly
*/
complete: false,
/**
* @property {Boolean} success
* Property tracking whether the Operation was successful or not. This starts as undefined and is set to `true`
* or `false` by the Proxy that is executing the Operation. It is also set to false by {@link #setException}. Use
* {@link #wasSuccessful} to query success status.
* @private
* @readonly
*/
success: undefined,
/**
* @property {Boolean} exception
* Property tracking the exception status of this Operation. Use {@link #hasException} and see {@link #getError}.
* @private
* @readonly
*/
exception: false,
/**
* @property {String/Object} error
* The error object passed when {@link #setException} was called. This could be any object or primitive.
* @private
*/
error: undefined,
/**
* Creates new Operation object.
* @param {Object} config (optional) Config object.
*/
constructor: function(config) {
this.initConfig(config);
},
applyModel: function(model) {
if (typeof model == 'string') {
model = Ext.data.ModelManager.getModel(model);
if (!model) {
Ext.Logger.error('Model with name ' + arguments[0] + ' doesnt exist.');
}
}
if (model && !model.prototype.isModel && Ext.isObject(model)) {
model = Ext.data.ModelManager.registerType(model.storeId || model.id || Ext.id(), model);
}
// <debug>
if (!model) {
Ext.Logger.warn('Unless you define your model using metadata, an Operation needs to have a model defined.');
}
// </debug>
return model;
},
getRecords: function() {
var resultSet = this.getResultSet();
return this._records || (resultSet ? resultSet.getRecords() : []);
},
/**
* Marks the Operation as started.
*/
setStarted: function() {
this.started = true;
this.running = true;
},
/**
* Marks the Operation as completed.
*/
setCompleted: function() {
this.complete = true;
this.running = false;
},
/**
* Marks the Operation as successful.
*/
setSuccessful: function() {
this.success = true;
},
/**
* Marks the Operation as having experienced an exception. Can be supplied with an option error message/object.
* @param {String/Object} error (optional) error string/object
*/
setException: function(error) {
this.exception = true;
this.success = false;
this.running = false;
this.error = error;
},
/**
* Returns `true` if this Operation encountered an exception (see also {@link #getError}).
* @return {Boolean} `true` if there was an exception.
*/
hasException: function() {
return this.exception === true;
},
/**
* Returns the error string or object that was set using {@link #setException}.
* @return {String/Object} The error object.
*/
getError: function() {
return this.error;
},
/**
* Returns `true` if the Operation has been started. Note that the Operation may have started AND completed, see
* {@link #isRunning} to test if the Operation is currently running.
* @return {Boolean} `true` if the Operation has started
*/
isStarted: function() {
return this.started === true;
},
/**
* Returns `true` if the Operation has been started but has not yet completed.
* @return {Boolean} `true` if the Operation is currently running
*/
isRunning: function() {
return this.running === true;
},
/**
* Returns `true` if the Operation has been completed
* @return {Boolean} `true` if the Operation is complete
*/
isComplete: function() {
return this.complete === true;
},
/**
* Returns `true` if the Operation has completed and was successful
* @return {Boolean} `true` if successful
*/
wasSuccessful: function() {
return this.isComplete() && this.success === true;
},
/**
* Checks whether this operation should cause writing to occur.
* @return {Boolean} Whether the operation should cause a write to occur.
*/
allowWrite: function() {
return this.getAction() != 'read';
},
process: function(action, resultSet, request, response) {
if (resultSet.getSuccess() !== false) {
this.setResponse(response);
this.setResultSet(resultSet);
this.setCompleted();
this.setSuccessful();
} else {
return false;
}
return this['process' + Ext.String.capitalize(action)].call(this, resultSet, request, response);
},
processRead: function(resultSet) {
var records = resultSet.getRecords(),
processedRecords = [],
Model = this.getModel(),
ln = records.length,
i, record;
for (i = 0; i < ln; i++) {
record = records[i];
processedRecords.push(new Model(record.data, record.id, record.node));
}
this.setRecords(processedRecords);
resultSet.setRecords(processedRecords);
return true;
},
processCreate: function(resultSet) {
var updatedRecords = resultSet.getRecords(),
currentRecords = this.getRecords(),
ln = updatedRecords.length,
i, currentRecord, updatedRecord;
for (i = 0; i < ln; i++) {
updatedRecord = updatedRecords[i];
if (updatedRecord.clientId === null && currentRecords.length == 1 && updatedRecords.length == 1) {
currentRecord = currentRecords[i];
} else {
currentRecord = this.findCurrentRecord(updatedRecord.clientId);
}
if (currentRecord) {
this.updateRecord(currentRecord, updatedRecord);
}
// <debug>
else {
Ext.Logger.warn('Unable to match the record that came back from the server.');
}
// </debug>
}
return true;
},
processUpdate: function(resultSet) {
var updatedRecords = resultSet.getRecords(),
currentRecords = this.getRecords(),
ln = updatedRecords.length,
i, currentRecord, updatedRecord;
for (i = 0; i < ln; i++) {
updatedRecord = updatedRecords[i];
currentRecord = currentRecords[i];
if (currentRecord) {
this.updateRecord(currentRecord, updatedRecord);
}
// <debug>
else {
Ext.Logger.warn('Unable to match the updated record that came back from the server.');
}
// </debug>
}
return true;
},
processDestroy: function(resultSet) {
var updatedRecords = resultSet.getRecords(),
ln = updatedRecords.length,
i, currentRecord, updatedRecord;
for (i = 0; i < ln; i++) {
updatedRecord = updatedRecords[i];
currentRecord = this.findCurrentRecord(updatedRecord.id);
if (currentRecord) {
currentRecord.setIsErased(true);
currentRecord.notifyStores('afterErase', currentRecord);
}
// <debug>
else {
Ext.Logger.warn('Unable to match the destroyed record that came back from the server.');
}
// </debug>
}
},
findCurrentRecord: function(clientId) {
var currentRecords = this.getRecords(),
ln = currentRecords.length,
i, currentRecord;
for (i = 0; i < ln; i++) {
currentRecord = currentRecords[i];
if (currentRecord.getId() === clientId) {
return currentRecord;
}
}
},
updateRecord: function(currentRecord, updatedRecord) {
var recordData = updatedRecord.data,
recordId = updatedRecord.id;
currentRecord.beginEdit();
currentRecord.set(recordData);
if (recordId !== null) {
currentRecord.setId(recordId);
}
// We call endEdit with silent: true because the commit below already makes
// sure any store is notified of the record being updated.
currentRecord.endEdit(true);
currentRecord.commit();
}
// <deprecated product=touch since=2.0>
}, function() {
/**
* @member Ext.data.Operation
* @cfg {Boolean} group
* @inheritdoc Ext.data.Operation#grouper
* @deprecated 2.0.0 Please use {@link #grouper} instead.
*/
Ext.deprecateProperty(this, 'group', 'grouper');
// </deprecated>
});