/** * ProxyStore is a superclass of {@link Ext.data.Store} and {@link Ext.data.BufferedStore}. * It's never used directly, but offers a set of methods used by both of those subclasses. * * We've left it here in the docs for reference purposes, but unless you need to make a whole new * type of Store, what you're probably looking for is {@link Ext.data.Store}. If you're still * interested, here's a brief description of what ProxyStore is and is not. * * ProxyStore provides the basic configuration for anything that can be considered a Store. * It expects to be given a {@link Ext.data.Model Model} that represents the type of data * in the Store. It also expects to be given a {@link Ext.data.proxy.Proxy Proxy} that handles * the loading of data into the Store. * * ProxyStore provides a few helpful methods such as {@link #method-load} and {@link #sync}, * which load and save data respectively, passing the requests through the configured * {@link #proxy}. * * Built-in Store subclasses add extra behavior to each of these functions. Note also that each * ProxyStore subclass has its own way of storing data - in {@link Ext.data.Store} the data * is saved as a flat {@link Ext.util.Collection Collection}, whereas in * {@link Ext.data.BufferedStore BufferedStore} we use a {@link Ext.data.PageMap} to maintain * a client side cache of pages of records. * * The store provides filtering and sorting support. This sorting/filtering can happen on the * client side or can be completed on the server. This is controlled by the * {@link Ext.data.Store#remoteSort remoteSort} and {@link Ext.data.Store#remoteFilter remoteFilter} * config options. For more information see the {@link #method-sort} and * {@link Ext.data.Store#filter filter} methods. */Ext.define('Ext.data.ProxyStore', { extend: 'Ext.data.AbstractStore', requires: [ 'Ext.data.Model', 'Ext.data.proxy.Proxy', 'Ext.data.proxy.Memory', 'Ext.data.operation.*' ], config: { // @cmd-auto-dependency {aliasPrefix: "model.", mvc: true, blame: "all"} /** * @cfg {String/Ext.data.Model} model * Name of the {@link Ext.data.Model Model} associated with this store. See * {@link Ext.data.Model#entityName}. * * May also be the actual Model subclass. * * This config is required for the store to be able to read data unless you have * defined the {@link #fields} config which will create an anonymous * `Ext.data.Model`. */ model: undefined, // @cmd-auto-dependency {aliasPrefix: "data.field."} /** * @cfg fields * @inheritdoc Ext.data.Model#cfg-fields * * @localdoc **Note:** In general, this configuration option should only be used * for simple stores like a two-field store of * {@link Ext.form.field.ComboBox ComboBox}. For anything more complicated, such * as specifying a particular id property or associations, a * {@link Ext.data.Model Model} should be defined and specified for the * {@link #model} config. * * @since 2.3.0 */ fields: null, // @cmd-auto-dependency {aliasPrefix : "proxy."} /** * @cfg {String/Ext.data.proxy.Proxy/Object} proxy * The Proxy to use for this Store. This can be either a string, a config object * or a Proxy instance - see {@link #setProxy} for details. * @since 1.1.0 */ proxy: undefined, /** * @cfg {Boolean/Object} autoLoad * If data is not specified, and if autoLoad is true or an Object, this store's * load method is automatically called after creation. If the value of autoLoad * is an Object, this Object will be passed to the store's load method. * * It's important to note that {@link Ext.data.TreeStore Tree Stores} will * load regardless of autoLoad's value if expand is set to true on the * {@link Ext.data.TreeStore#root root node}. * * @since 2.3.0 */ autoLoad: undefined, /** * @cfg {Boolean} autoSync * True to automatically sync the Store with its Proxy after every edit to one of * its Records. Defaults to false. */ autoSync: false, /** * @cfg {String} batchUpdateMode * Sets the updating behavior based on batch synchronization. 'operation' (the * default) will update the Store's internal representation of the data after * each operation of the batch has completed, 'complete' will wait until the * entire batch has been completed before updating the Store's data. 'complete' * is a good choice for local storage proxies, 'operation' is better for remote * proxies, where there is a comparatively high latency. */ batchUpdateMode: 'operation', /** * @cfg {Boolean} sortOnLoad * If true, any sorters attached to this Store will be run after loading data, * before the datachanged event is fired. Defaults to true, ignored if * {@link Ext.data.Store#remoteSort remoteSort} is true */ sortOnLoad: true, /** * @cfg {Boolean} trackRemoved * This config controls whether removed records are remembered by this store for * later saving to the server. */ trackRemoved: true, /** * @cfg {Boolean} asynchronousLoad * This defaults to `true` when this store's {@link #cfg-proxy} is asynchronous, * such as an {@link Ext.data.proxy.Ajax Ajax proxy}. * * When the proxy is synchronous, such as a {@link Ext.data.proxy.Memory} memory * proxy, this defaults to `false`. * * *NOTE:* This does not cause synchronous Ajax requests if configured `false` * when an Ajax proxy is used. It causes immediate issuing of an Ajax request * when {@link #method-load} is called rather than issuing the request at the end * of the current event handler run. * * What this means is that when using an Ajax proxy, calls to * {@link #method-load} do not fire the request to the remote resource * immediately, but schedule a request to be made. This is so that multiple * requests are not fired when mutating a store's remote filters and sorters (as * happens during state restoration). The request is made only once after all * relevant store state is fully set. * * @since 6.0.1 */ asynchronousLoad: undefined }, onClassExtended: function(cls, data, hooks) { var model = data.model, onBeforeClassCreated; if (typeof model === 'string') { onBeforeClassCreated = hooks.onBeforeCreated; hooks.onBeforeCreated = function() { var me = this, args = arguments; Ext.require(model, function() { onBeforeClassCreated.apply(me, args); }); }; } }, /** * @private * @property {Boolean} implicitModel * The class name of the model that this store uses if no explicit {@link #model} is * given */ implicitModel: 'Ext.data.Model', /** * @property {Object} lastOptions * Property to hold the last options from a {@link #method-load} method call. This * object is used for the {@link #method-reload} to reuse the same options. Please * see {@link #method-reload} for a simple example on how to use the lastOptions * property. */ /** * @property {Number} autoSyncSuspended * A counter to track suspensions. * @private */ autoSyncSuspended: 0, /** * @property {Ext.data.Model[]} removed * Temporary cache in which removed model instances are kept until successfully * synchronised with a Proxy, at which point this is cleared. * * This cache is maintained unless you set `trackRemoved` to `false`. * * @protected * @readonly */ removed: null, /** * @event beforeload * Fires before a request is made for a new data object. If the beforeload handler returns * `false` the load action will be canceled. * * **Note:** If you are using a buffered store, you should use * {@link Ext.data.Store#beforeprefetch beforeprefetch}. * @param {Ext.data.Store} store This Store * @param {Ext.data.operation.Operation} operation The Ext.data.operation.Operation object * that will be passed to the Proxy to load the Store * @since 1.1.0 */ /** * @event load * Fires whenever the store reads data from a remote data source. * * **Note:** If you are using a buffered store, you should use * {@link Ext.data.Store#prefetch prefetch}. * @param {Ext.data.Store} this * @param {Ext.data.Model[]} records An array of records * @param {Boolean} successful True if the operation was successful. * @param {Ext.data.operation.Read} operation The * {@link Ext.data.operation.Read Operation} object that was used in the data * load call * @since 1.1.0 */ /** * @event write * Fires whenever a successful write has been made via the configured {@link #proxy Proxy} * @param {Ext.data.Store} store This Store * @param {Ext.data.operation.Operation} operation The * {@link Ext.data.operation.Operation Operation} object that was used in the write * @since 3.4.0 */ /** * @event beforesync * Fired before a call to {@link #sync} is executed. Return false from any listener to cancel * the sync * @param {Object} options Hash of all records to be synchronized, broken down into create, * update and destroy */ /** * @event metachange * Fires when this store's underlying reader (available via the proxy) provides new metadata. * Metadata usually consists of new field definitions, but can include any configuration data * required by an application, and can be processed as needed in the event handler. * This event is currently only fired for JsonReaders. * @param {Ext.data.Store} this * @param {Object} meta The JSON metadata * @since 1.1.0 */ constructor: function(config) { var me = this; //<debug> var configModel = me.model; // eslint-disable-line vars-on-top, one-var //</debug> me.callParent(arguments); if (me.getAsynchronousLoad() === false) { me.flushLoad(); } //<debug> if (!me.getModel() && me.useModelWarning !== false && me.getStoreId() !== 'ext-empty-store') { // There are a number of ways things could have gone wrong, try to give as much // information as possible var logMsg = [ // eslint-disable-line vars-on-top, one-var Ext.getClassName(me) || 'Store', ' created with no model.' ]; if (typeof configModel === 'string') { logMsg.push(" The name '", configModel, "'", ' does not correspond to a valid model.'); } Ext.log.warn(logMsg.join('')); } //</debug> }, /** * @private */ doDestroy: function() { var me = this, proxy = me.getProxy(); me.clearLoadTask(); Ext.destroy(me.getData()); me.data = null; me.setProxy(null); if (proxy.autoCreated) { proxy.destroy(); } me.setModel(null); me.callParent(); }, applyAsynchronousLoad: function(asynchronousLoad) { // Default in an asynchronousLoad setting. // It defaults to false if the proxy is synchronous, and true if the proxy is asynchronous. if (asynchronousLoad == null) { asynchronousLoad = !this.loadsSynchronously(); } return asynchronousLoad; }, updateAutoLoad: function(autoLoad) { // Ensure the data collection is set up this.getData(); if (autoLoad) { // Defer the load until idle, when the store (and probably the view) // is fully constructed this.load(Ext.isObject(autoLoad) ? autoLoad : undefined); } }, /** * Returns the total number of {@link Ext.data.Model Model} instances that the * {@link Ext.data.proxy.Proxy Proxy} indicates exist. This will usually differ from * {@link #getCount} when using paging - getCount returns the number of records loaded into * the Store at the moment, getTotalCount returns the number of records that could be loaded * into the Store if the Store contained all data * @return {Number} The total number of Model instances available via the Proxy. 0 returned if * no value has been set via the reader. */ getTotalCount: function() { return this.totalCount || 0; }, applyFields: function(fields) { if (fields) { this.createImplicitModel(fields); } }, applyModel: function(model) { if (model) { model = Ext.data.schema.Schema.lookupEntity(model); } else if (!this.destroying) { // If no model, ensure that the fields config is converted to a model. this.getFields(); model = this.getModel() || this.createImplicitModel(); } return model; }, applyProxy: function(proxy) { var model = this.getModel(); if (proxy !== null) { if (proxy) { if (proxy.isProxy) { proxy.setModel(model); } else { if (Ext.isString(proxy)) { proxy = { type: proxy, model: model }; } else if (!proxy.model) { proxy = Ext.apply({ model: model }, proxy); } proxy = Ext.createByAlias('proxy.' + proxy.type, proxy); proxy.autoCreated = true; } } else if (model) { proxy = model.getProxy(); this.useModelProxy = true; } if (!proxy) { proxy = Ext.createByAlias('proxy.memory'); proxy.autoCreated = true; } } return proxy; }, applyState: function(state) { var me = this; me.callParent([state]); // This is called during construction. Sorters and filters might have changed // which require a reload. // If autoLoad is true, it might have loaded synchronously from a memory proxy, // so needs to reload. // If it is already loaded, we definitely need to reload to apply the state. // If we're not remote sorting or filtering, there is no point in loading. if ((me.getAutoLoad() || me.isLoaded()) && (me.getRemoteSort() || me.getRemoteFilter())) { me.load(); } }, updateProxy: function(proxy, oldProxy) { this.proxyListeners = Ext.destroy(this.proxyListeners); }, updateTrackRemoved: function(track) { this.cleanRemoved(); this.removed = track ? [] : null; }, /** * @private */ onMetaChange: function(proxy, meta) { this.fireEvent('metachange', this, meta); }, // saves any phantom records create: function(data, options) { var me = this, Model = me.getModel(), instance = new Model(data), operation; options = Ext.apply({}, options); if (!options.records) { options.records = [instance]; } options.internalScope = me; options.internalCallback = me.onProxyWrite; operation = me.createOperation('create', options); return operation.execute(); }, read: function() { return this.load.apply(this, arguments); }, update: function(options) { var me = this, operation; options = Ext.apply({}, options); if (!options.records) { options.records = me.getUpdatedRecords(); } options.internalScope = me; options.internalCallback = me.onProxyWrite; operation = me.createOperation('update', options); return operation.execute(); }, /** * @private * Callback for any write Operation over the Proxy. Updates the Store's MixedCollection * to reflect the updates provided by the Proxy */ onProxyWrite: function(operation) { var me = this, success = operation.wasSuccessful(), records = operation.getRecords(); switch (operation.getAction()) { case 'create': me.onCreateRecords(records, operation, success); break; case 'update': me.onUpdateRecords(records, operation, success); break; case 'destroy': me.onDestroyRecords(records, operation, success); break; } if (success) { me.fireEvent('write', me, operation); me.fireEvent('datachanged', me); } }, // may be implemented by store subclasses onCreateRecords: Ext.emptyFn, // may be implemented by store subclasses onUpdateRecords: Ext.emptyFn, /** * Removes any records when a write is returned from the server. * @private * @param {Ext.data.Model[]} records The array of removed records * @param {Ext.data.operation.Operation} operation The operation that just completed * @param {Boolean} success True if the operation was successful */ onDestroyRecords: function(records, operation, success) { if (success) { this.cleanRemoved(); } }, // tells the attached proxy to destroy the given records // @since 3.4.0 erase: function(options) { var me = this, operation; options = Ext.apply({}, options); if (!options.records) { options.records = me.getRemovedRecords(); } options.internalScope = me; options.internalCallback = me.onProxyWrite; operation = me.createOperation('destroy', options); return operation.execute(); }, /** * @private * Attached as the 'operationcomplete' event listener to a proxy's Batch object. By default * just calls through to onProxyWrite. */ onBatchOperationComplete: function(batch, operation) { return this.onProxyWrite(operation); }, /** * @private * Attached as the 'complete' event listener to a proxy's Batch object. Iterates over the batch * operations and updates the Store's internal data MixedCollection. */ onBatchComplete: function(batch, operation) { var me = this, operations = batch.operations, length = operations.length, i; if (me.batchUpdateMode !== 'operation') { me.suspendEvents(); for (i = 0; i < length; i++) { me.onProxyWrite(operations[i]); } me.resumeEvents(); } me.isSyncing = false; if (batch.$destroyOwner === me) { batch.destroy(); } me.fireEvent('datachanged', me); }, /** * @private */ onBatchException: function(batch, operation) { // //decide what to do... could continue with the next operation // batch.start(); // // //or retry the last operation // batch.retry(); }, /** * @private * Filter function for new records. */ filterNew: function(item) { // only want phantom records that are valid return item.phantom && item.isValid(); }, /** * Returns all `{@link Ext.data.Model#property-phantom phantom}` records in this store. * @return {Ext.data.Model[]} A possibly empty array of `phantom` records. */ getNewRecords: function() { return []; }, /** * Returns all valid, non-phantom Model instances that have been updated in the Store but * not yet synchronized with the Proxy. * @return {Ext.data.Model[]} The updated Model instances */ getUpdatedRecords: function() { return []; }, /** * Gets all {@link Ext.data.Model records} added or updated since the last commit. Note that * the order of records returned is not deterministic and does not indicate the order in which * records were modified. Note also that removed records are not included * (use {@link #getRemovedRecords} for that). * @return {Ext.data.Model[]} The added and updated Model instances */ getModifiedRecords: function() { return [].concat(this.getNewRecords(), this.getUpdatedRecords()); }, /** * @private * Filter function for updated records. */ filterUpdated: function(item) { // only want dirty records, not phantoms that are valid return item.dirty && !item.phantom && item.isValid(); }, /** * Returns any records that have been removed from the store but not yet destroyed on the proxy. * @return {Ext.data.Model[]} The removed Model instances. Note that this is a *copy* of the * store's array, so may be mutated. */ getRemovedRecords: function() { var removed = this.getRawRemovedRecords(); return removed ? Ext.Array.clone(removed) : []; }, /** * Synchronizes the store with its {@link #proxy}. This asks the proxy to batch together any * new, updated and deleted records in the store, updating the store's internal representation * of the records as each operation completes. * * @param {Object} [options] Object containing one or more properties supported by the sync * method (these get passed along to the underlying proxy's {@link Ext.data.Proxy#batch batch} * method): * * @param {Ext.data.Batch/Object} [options.batch] A {@link Ext.data.Batch} object (or batch * config to apply to the created batch). If unspecified a default batch will be auto-created * as needed. * * @param {Function} [options.callback] The function to be called upon completion of the sync. * The callback is called regardless of success or failure and is passed the following * parameters: * @param {Ext.data.Batch} options.callback.batch The {@link Ext.data.Batch batch} that was * processed, containing all operations in their current state after processing * @param {Object} options.callback.options The options argument that was originally passed * into sync * * @param {Function} [options.success] The function to be called upon successful completion * of the sync. The success function is called only if no exceptions were reported in any * operations. If one or more exceptions occurred then the failure function will be called * instead. The success function is called with the following parameters: * @param {Ext.data.Batch} options.success.batch The {@link Ext.data.Batch batch} that was * processed, containing all operations in their current state after processing * @param {Object} options.success.options The options argument that was originally passed * into sync * * @param {Function} [options.failure] The function to be called upon unsuccessful completion * of the sync. The failure function is called when one or more operations returns an exception * during processing (even if some operations were also successful). In this case you can check * the batch's {@link Ext.data.Batch#exceptions exceptions} array to see exactly which * operations had exceptions. The failure function is called with the following parameters: * @param {Ext.data.Batch} options.failure.batch The {@link Ext.data.Batch} that was processed, * containing all operations in their current state after processing * @param {Object} options.failure.options The options argument that was originally passed * into sync * * @param {Object} [options.params] Additional params to send during the sync Operation(s). * * @param {Object} [options.scope] The scope in which to execute any callbacks (i.e. the `this` * object inside the callback, success and/or failure functions). Defaults to the store's proxy. * * @return {Ext.data.Store} this */ sync: function(options) { var me = this, operations = {}, toCreate = me.getNewRecords(), toUpdate = me.getUpdatedRecords(), toDestroy = me.getRemovedRecords(), needsSync = false; //<debug> if (me.isSyncing) { Ext.log.warn('Sync called while a sync operation is in progress. ' + 'Consider configuring autoSync as false.'); } //</debug> me.needsSync = false; if (toCreate.length > 0) { operations.create = toCreate; needsSync = true; } if (toUpdate.length > 0) { operations.update = toUpdate; needsSync = true; } if (toDestroy.length > 0) { operations.destroy = toDestroy; needsSync = true; } if (needsSync && me.fireEvent('beforesync', operations) !== false) { me.isSyncing = true; options = options || {}; me.proxy.batch(Ext.apply(options, { operations: operations, listeners: me.getBatchListeners(), $destroyOwner: me })); } return me; }, /** * @private * Returns an object which is passed in as the listeners argument to proxy.batch inside * this.sync. This is broken out into a separate function to allow for customisation * of the listeners * @return {Object} The listeners object */ getBatchListeners: function() { var me = this, listeners = { scope: me, exception: me.onBatchException, complete: me.onBatchComplete }; if (me.batchUpdateMode === 'operation') { listeners.operationcomplete = me.onBatchOperationComplete; } return listeners; }, /** * Saves all pending changes via the configured {@link #proxy}. Use {@link #sync} instead. * @deprecated 4.0.0 Will be removed in the next major version */ save: function() { return this.sync.apply(this, arguments); }, /** * Marks this store as needing a load. When the current executing event handler exits, * this store will send a request to load using its configured {@link #proxy}. * * Upon return of the data from whatever data source the proxy connected to, the retrieved * {@link Ext.data.Model records} will be loaded into this store, and the optional callback * will be called. Example usage: * * store.load({ * scope: this, * callback: function(records, operation, success) { * // the operation object * // contains all of the details of the load operation * console.log(records); * } * }); * * If the callback scope does not need to be set, a function can simply be passed: * * store.load(function(records, operation, success) { * console.log('loaded records'); * }); * * @param {Object} [options] This is passed into the * {@link Ext.data.operation.Operation Operation} object that is created and then sent to the * proxy's {@link Ext.data.proxy.Proxy#read} function. In addition to the options listed below, * this object may contain properties to configure the * {@link Ext.data.operation.Operation Operation}. * @param {Function} [options.callback] A function which is called when the response arrives. * @param {Ext.data.Model[]} options.callback.records Array of records. * @param {Ext.data.operation.Operation} options.callback.operation The Operation itself. * @param {Boolean} options.callback.success `true` when operation completed successfully. * @param {Boolean} [options.addRecords=false] Specify as `true` to *add* the incoming records * rather than the default which is to have the incoming records *replace* the existing store * contents. * * @return {Ext.data.Store} this * @since 1.1.0 */ load: function(options) { var me = this; // Legacy option. Specifying a function was allowed. if (typeof options === 'function') { options = { callback: options }; } else { // We may mutate the options object in setLoadOptions. options = options ? Ext.Object.chain(options) : {}; } me.pendingLoadOptions = options; // If we are configured to load asynchronously (the default for async proxies) // then schedule a flush, unless one is already scheduled. if (me.getAsynchronousLoad()) { if (!me.loadTimer) { me.loadTimer = Ext.asap(me.flushLoad, me); } } // If we are configured to load synchronously (the default for sync proxies) // then flush the load now. else { me.flushLoad(); } return me; }, /** * Called when the event handler which called the {@link #method-load} method exits. */ flushLoad: function() { var me = this, options = me.pendingLoadOptions, operation; if (me.destroying || me.destroyed) { return; } // If it gets called programatically before the timer fired, the listener will need // cancelling. me.clearLoadTask(); if (!options) { return; } me.setLoadOptions(options); if (me.getRemoteSort() && options.sorters) { me.fireEvent('beforesort', me, options.sorters); } operation = Ext.apply({ internalScope: me, internalCallback: me.onProxyLoad, scope: me }, options); me.lastOptions = operation; operation = me.createOperation('read', operation); if (me.fireEvent('beforeload', me, operation) !== false) { me.onBeforeLoad(operation); me.loading = true; // Internal event, fired after the flag is set, we need // to fire this beforeload is too early if (me.hasListeners.beginload) { me.fireEvent('beginload', me, operation); } operation.execute(); } else { if (me.getAsynchronousLoad()) { operation.abort(); } operation.setCompleted(); } }, /** * Reloads the store using the last options passed to the {@link #method-load} method. * You can use the reload method to reload the store using the parameters from the last load() * call. For example: * * store.load({ * params : { * userid : 22216 * } * }); * * //... * * store.reload(); * * The initial {@link #method-load} execution will pass the `userid` parameter in the request. * The {@link #reload} execution will also send the same `userid` parameter in its request * as it will reuse the `params` object from the last {@link #method-load} call. * * You can override a param by passing in the config object with the `params` object: * * store.load({ * params : { * userid : 22216, * foo : 'bar' * } * }); * * //... * * store.reload({ * params : { * userid : 1234 * } * }); * * The initial {@link #method-load} execution sends the `userid` and `foo` parameters but in the * {@link #reload} it only sends the `userid` paramter because you are overriding the `params` * config not just overriding the one param. To only change a single param but keep other * params, you will have to get the last params from the {@link #lastOptions} property: * * // make a copy of the last params so we don't affect future reload() calls * var lastOptions = store.lastOptions, * lastParams = Ext.clone(lastOptions.params); * * lastParams.userid = 1234; * * store.reload({ * params : lastParams * }); * * This will now send the `userid` parameter as `1234` and the `foo` param as `'bar'`. * * @param {Object} [options] A config object which contains options which may override the * options passed to the previous load call. See the * {@link #method-load} method for valid configs. */ reload: function(options) { return this.load(Ext.apply({}, options, this.lastOptions)); }, onEndUpdate: function() { var me = this; if (me.needsSync && me.autoSync && !me.autoSyncSuspended) { me.sync(); } }, /** * @private * A model instance should call this method on the Store it has been * {@link Ext.data.Model#join joined} to. * @param {Ext.data.Model} record The model instance that was edited * @since 3.4.0 */ afterReject: function(record) { var me = this; // Must pass the 5th param (modifiedFieldNames) as null, otherwise the // event firing machinery appends the listeners "options" object to the arg list // which may get used as the modified fields array by a handler. // This array is used for selective grid cell updating by Grid View. // Null will be treated as though all cells need updating. if (me.contains(record)) { me.onUpdate(record, Ext.data.Model.REJECT, null); me.fireEvent('update', me, record, Ext.data.Model.REJECT, null); me.fireEvent('datachanged', me); } }, /** * A model instance should call this method on the Store it has been * {@link Ext.data.Model#join joined} to. * @param {Ext.data.Model} record The model instance that was edited. * @param {String[]} [modifiedFieldNames] (private) * @since 3.4.0 * @private */ afterCommit: function(record, modifiedFieldNames) { var me = this; if (!modifiedFieldNames) { modifiedFieldNames = null; } if (me.contains(record)) { me.onUpdate(record, Ext.data.Model.COMMIT, modifiedFieldNames); me.fireEvent('update', me, record, Ext.data.Model.COMMIT, modifiedFieldNames); me.fireEvent('datachanged', me); } }, afterErase: function(record) { this.onErase(record); }, onErase: Ext.emptyFn, onUpdate: Ext.emptyFn, /** * Returns true if the store has a pending load task. * @return {Boolean} `true` if the store has a pending load task. * @private */ hasPendingLoad: function() { return !!this.pendingLoadOptions || this.isLoading(); }, /** * Returns true if the Store is currently performing a load operation * @return {Boolean} `true` if the Store is currently loading */ isLoading: function() { return !!this.loading; }, /** * Returns `true` if the Store has been loaded. * @return {Boolean} `true` if the Store has been loaded. */ isLoaded: function() { return this.loadCount > 0; }, /** * Suspends automatically syncing the Store with its Proxy. Only applicable if * {@link #autoSync} is `true` */ suspendAutoSync: function() { ++this.autoSyncSuspended; }, /** * Resumes automatically syncing the Store with its Proxy. Only applicable if * {@link #autoSync} is `true` * @param {Boolean} syncNow Pass `true` to synchronize now. Only synchronizes with the Proxy * if the suspension count has gone to zero (We are not under a higher level of suspension) * */ resumeAutoSync: function(syncNow) { var me = this; //<debug> if (!me.autoSyncSuspended) { Ext.log.warn('Mismatched call to resumeAutoSync - auto synchronization ' + 'is currently not suspended.'); } //</debug> if (me.autoSyncSuspended && ! --me.autoSyncSuspended) { if (syncNow) { me.sync(); } } }, /** * Removes all records from the store. This method does a "fast remove", * individual remove events are not called. The {@link #clear} event is * fired upon completion. * @method * @since 1.1.0 */ removeAll: Ext.emptyFn, // individual store subclasses should implement a "fast" remove // and fire a clear event afterwards // to be implemented by subclasses clearData: Ext.emptyFn, privates: { /** * @private * Returns the array of records which have been removed since the last time this store * was synced. * * This is used internally, when purging removed records after a successful sync. * This is overridden by TreeStore because TreeStore accumulates deleted records on removal * of child nodes from their parent, *not* on removal of records from its collection. * The collection has records added on expand, and removed on collapse. */ getRawRemovedRecords: function() { return this.removed; }, onExtraParamsChanged: function() { }, clearLoadTask: function() { this.pendingLoadOptions = this.loadTimer = Ext.unasap(this.loadTimer); }, cleanRemoved: function() { // Must use class-specific getRawRemovedRecords. // Regular Stores add to the "removed" property on remove. // TreeStores are having records removed all the time; node collapse removes. // TreeStores add to the "removedNodes" property onNodeRemove var removed = this.getRawRemovedRecords(), len, i; if (removed) { for (i = 0, len = removed.length; i < len; ++i) { removed[i].unjoin(this); } removed.length = 0; } }, createOperation: function(type, options) { var me = this, proxy = me.getProxy(), listeners; if (!me.proxyListeners) { listeners = { scope: me, destroyable: true, beginprocessresponse: me.beginUpdate, endprocessresponse: me.endUpdate }; if (!me.disableMetaChangeEvent) { listeners.metachange = me.onMetaChange; } me.proxyListeners = proxy.on(listeners); } return proxy.createOperation(type, options); }, createImplicitModel: function(fields) { var me = this, modelCfg = { extend: me.implicitModel, statics: { defaultProxy: 'memory' } }, proxy, model; if (fields) { modelCfg.fields = fields; } model = Ext.define(null, modelCfg); me.setModel(model); proxy = me.getProxy(); if (proxy) { model.setProxy(proxy); } else { me.setProxy(model.getProxy()); } }, loadsSynchronously: function() { return this.getProxy().isSynchronous; }, onBeforeLoad: Ext.privateFn, removeFromRemoved: function(record) { // Must use class-specific getRawRemovedRecords. // Regular Stores add to the "removed" property on remove. // TreeStores are having records removed all the time; node collapse removes. // TreeStores add to the "removedNodes" property onNodeRemove var removed = this.getRawRemovedRecords(); if (removed) { Ext.Array.remove(removed, record); record.unjoin(this); } }, setLoadOptions: function(options) { var me = this, filters, sorters; if (me.getRemoteFilter()) { filters = me.getFilters(false); if (filters && filters.getCount()) { options.filters = filters.getRange(); } } if (me.getRemoteSort()) { sorters = me.getSorters(false); if (sorters && sorters.getCount()) { options.sorters = sorters.getRange(); } } } } });