/**
* This mixin provides a `dirty` config that tracks the modified state of an object. If
* the class using this mixin is {@link Ext.mixin.Observable observable}, changes to the
* `dirty` config will fire the `dirtychange` event.
* @protected
* @since 6.2.0
*/
Ext.define('Ext.mixin.Dirty', {
mixinId: 'dirty',
/**
* @event dirtychange
* Fires when a change in the object's {@link #cfg-dirty} state is detected.
*
* **Note:** In order for this event to fire, the class that mixes in this mixin
* must be `{@link Ext.mixin.Observable Observable}`.
*
* @param {Ext.Base} this
* @param {Boolean} dirty Whether or not the object is now dirty.
*/
config: {
/**
* @cfg {Boolean} dirty
* This config property describes the modified state of this object. In most
* cases this config's value is maintained by the object and should be considered
* readonly. The class implementor should be the only one to call the setter.
*/
dirty: {
$value: false,
lazy: true
}
},
dirty: false, // on the prototype as false (not undefined)
/**
* @property {Number} _dirtyRecordCount
* The number of newly created, modified or dropped records.
* @private
* @readonly
*/
_dirtyRecordCount: 0,
/**
* @cfg {Boolean} ignoreDirty
* This config property indicates that the `dirty` state of this object should be
* ignored. Because this capability is mixed in at a class level, this config can
* be helpful when some instances do not participate in dirty state tracking.
*
* This option should be set at construction time. When set to `true`, the object
* will always have `dirty` value of `false`.
*/
ignoreDirty: false,
/**
* @cfg {Boolean} recordStateIsDirtyState
* Set this config at construction time (or on the class body) to automatically set
* the `dirty` state based on the records passed to `trackRecordState`.
*
* This config defaults to `true` but only has an effect when the record tracking
* methods are called (`trackRecordState`, `untrackRecordState` and `clearRecordStates`).
* @protected
*/
recordStateIsDirtyState: true,
/**
* Returns `true` if this object is `dirty`.
*/
isDirty: function() {
// This alias matches the Ext.form.field.* family.
return this.getDirty();
},
applyDirty: function(dirty) {
return this.ignoreDirty ? false : dirty;
},
updateDirty: function(dirty) {
var me = this;
// Store the property directly in case we are used in an "_dirty" world.
me.dirty = dirty;
if (me.fireEvent && !me.isDirtyInitializing) {
me.fireDirtyChange();
}
},
/**
* Clears all record state tracking. This state is maintained by `trackRecordState`
* and `untrackRecordState`.
* @protected
*/
clearRecordStates: function() {
var me = this,
counters = me._crudCounters;
if (counters) {
counters.C = counters.U = counters.D = 0;
}
me._dirtyRecordCount = 0;
if (me.recordStateIsDirtyState) {
me.setDirty(false);
}
},
fireDirtyChange: function() {
var me = this;
if (!me.ignoreDirty && me.hasListeners.dirtychange) {
me.fireEvent('dirtychange', me, me.dirty);
}
},
/**
* This method is called to track a given record in the total number of dirty records
* (modified, created or dropped). See `untrackRecordState` and `clearRecordStates`.
*
* @param {Ext.data.Model} record The record to track.
* @param {Boolean} initial Pass `true` the first time a record is introduced.
* @return {Boolean} Returns `true` if the state of dirty records has changed.
* @protected
*/
trackRecordState: function(record, initial) {
var me = this,
counters = me._crudCounters || (me._crudCounters = { C: 0, R: 0, U: 0, D: 0 }),
dirtyRecordCountWas = me._dirtyRecordCount,
state = record.crudState,
stateWas = record.crudStateWas,
changed, dirtyRecordCount;
if (initial || state !== stateWas) {
if (!initial && stateWas) {
--counters[stateWas];
}
if (!(record.phantom && state === 'D')) {
++counters[state];
}
//<debug>
me.checkCounters();
//</debug>
me._dirtyRecordCount = dirtyRecordCount = counters.C + counters.U + counters.D;
changed = !dirtyRecordCount !== !dirtyRecordCountWas;
if (changed && me.recordStateIsDirtyState) {
me.setDirty(dirtyRecordCount > 0);
}
}
return changed;
},
/**
* This method is called to remove the tracking of a given record from the total number
* of dirty records (modified, created or dropped). The record passed to this method
* must have been previously passed to `trackRecordState`.
*
* @param {Ext.data.Model} record The record to stop tracking.
* @return {Boolean} Returns `true` if the state of dirty records has changed.
* @protected
*/
untrackRecordState: function(record) {
var me = this,
counters = me._crudCounters,
dirtyRecordCountWas = me._dirtyRecordCount,
state = record.crudState,
changed, dirtyRecordCount;
// If it's erased and dropped, it will have already been tracked
if (counters && state !== 'D' && !record.erased) {
--counters[state];
//<debug>
me.checkCounters();
//</debug>
me._dirtyRecordCount = dirtyRecordCount = counters.C + counters.U + counters.D;
changed = !dirtyRecordCount !== !dirtyRecordCountWas;
if (changed && me.recordStateIsDirtyState) {
me.setDirty(dirtyRecordCount > 0);
}
}
return changed;
}
//<debug>
, checkCounters: function() { // eslint-disable-line comma-style
var counters = this._crudCounters,
key;
for (key in counters) {
if (counters[key] < 0) {
Ext.raise('Invalid state for ' + key);
}
}
}
//</debug>
});