/** * This class provides access to a range of records in a {@link Ext.data.Store store}. * Instances of this class are not created directly but are rather returned by a store's * {@link Ext.data.AbstractStore#createActiveRange createActiveRange} method. * * This class is useful for buffered rendering where only a portion of the total set of * records are needed. By passing that information to a `Range`, the access to records * can be abstracted even across {@link Ext.data.virtual.Store virtual stores} where * only those records needed by views are fetched from the server. * @since 6.5.0 */Ext.define('Ext.data.Range', { requires: [ 'Ext.util.DelayedTask', 'Ext.Deferred' ], isDataRange: true, /** * @cfg {Number} begin * The first record index of interest. * * This property is set by the `goto` method and is stored on the instance for * readonly use. * @readonly */ begin: 0, /** * @cfg {Number} buffer * The buffer to execute server requests. */ buffer: 0, /** * @cfg {Number} end * The first record beyond the range of interest. This is to make "length" of the * range simply `end - begin`. * * This property is set by the `goto` method and is stored on the instance for * readonly use. */ end: 0, /** * @property (Number} length * The number of records in the range of `[begin, end)`. This is equal to the * difference `end - begin`. * * This property is maintained by the `goto` method and is stored on the instance for * readonly use. * @readonly */ length: 0, /** * @property {Ext.data.Model[]} records * The records corresponding to the `begin` and `end` of this range. For normal * stores this is the standard array of records. * * For a {@link Ext.data.virtual.Store virtual store} this is a sparse object of * available records bounded by the limits of this range. * * In all cases, this object is keyed by the record index and (except for the * `length` property) should be treated as an array. * @readonly */ /** * @cfg {Ext.data.AbstractStore} store * The associated store. This config must be supplied at construction and cannot * be changed after that time. * @readonly */ store: null, /** * @cfg {Number} waitTimeout * A timeout to wait for promises to complete returned from `goto`. `null` for * the timeout to be infinite. */ waitTimeout: 10000, // private activeWait: null, constructor: function(config) { var me = this, activeRanges, store; Ext.apply(me, config); store = me.store; if (!(activeRanges = store.activeRanges)) { store.activeRanges = activeRanges = []; } activeRanges.push(me); me.refresh(); if ('begin' in config) { me.begin = me.end = 0; // Applied on us above, so clear it me.goto(config.begin, config.end).catch(function() { // Do nothing, since this was passed as a config, we can't // reasonably return a failure here }); } }, destroy: function() { var me = this, store = me.store, activeRanges = store && store.activeRanges; Ext.destroy(me.storeListeners); if (activeRanges) { Ext.Array.remove(activeRanges, me); } me.cancelActiveWait(); me.callParent(); }, /** * Go to a particular point in the range. * @param {Number} begin The start index, inclusive. * @param {Number} end The end index, exclusive. * @return Ext.Promise The promise. Resolves with `this` when the range is resolved * successfully. Resolves with `null` if the range times out, or a new range is requested * before the current one completes. */ goto: function(begin, end) { var me = this, buffer = me.buffer, task = me.task, promise; me.begin = begin; me.end = end; me.length = end - begin; me.cancelActiveWait(); me.activeWait = me.setupWait(begin, end); // Need to assign this here. In a synchronous range, doGoto will resolve immediately. promise = me.activeWait.promise; me.resolveWaitIfSatisfied(); if (buffer > 0) { if (!task) { me.task = task = new Ext.util.DelayedTask(me.doGoto, me); } task.delay(buffer); } else { me.doGoto(); } return promise; }, privates: { lastBegin: 0, lastEnd: 0, cancelActiveWait: function() { this.resolveWait(null); }, doGoto: Ext.privateFn, refresh: function() { this.records = this.store.getData().items; }, resolveWait: function(value) { var wait = this.activeWait; if (wait) { wait.deferred.resolve(value); this.activeWait = null; } return value; }, resolveWaitIfSatisfied: function() { this.resolveWait(this); }, setupWait: function(begin, end) { var me = this, timeout = me.waitTimeout, deferred = new Ext.Deferred(), promise = deferred.promise; if (timeout) { promise = Ext.Deferred.timeout(promise, timeout).catch(function() { return me.resolveWait(null); }); } return { deferred: deferred, promise: promise }; } }});