13693261870
2022-09-16 354b3dbfbffb3df45212a2a44dbbf48b4acc2594
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
/*
This file is part of Ext JS 4.2
 
Copyright (c) 2011-2013 Sencha Inc
 
Contact:  http://www.sencha.com/contact
 
GNU General Public License Usage
This file may be used under the terms of the GNU General Public License version 3.0 as
published by the Free Software Foundation and appearing in the file LICENSE included in the
packaging of this file.
 
Please review the following information to ensure the GNU General Public License version 3.0
requirements will be met: http://www.gnu.org/copyleft/gpl.html.
 
If you are unsure which license is appropriate for your use, please contact the sales department
at http://www.sencha.com/contact.
 
Build date: 2013-05-16 14:36:50 (f9be68accb407158ba2b1be2c226a6ce1f649314)
*/
/**
 * @class Ext.data.PageMap
 * @extends Ext.util.LruCache
 * Private class for use by only Store when configured `buffered: true`.
 * @private
 */
Ext.define('Ext.data.PageMap', {
    extend: 'Ext.util.LruCache',
 
    // Maintain a generation counter, so that the Store can reject incoming pages destined for the previous generation
    clear: function(initial) {
        var me = this;
        me.pageMapGeneration = (me.pageMapGeneration || 0) + 1;
        me.callParent(arguments);
    },
 
    forEach: function(fn, scope) {
        var me = this,
            pageNumbers = Ext.Object.getKeys(me.map),
            pageCount = pageNumbers.length,
            i, j,
            pageNumber,
            page,
            pageSize;
 
        for (i = 0; i < pageCount; i++) {
            pageNumbers[i] = Number(pageNumbers[i]);
        }
        Ext.Array.sort(pageNumbers);
        scope = scope || me;
        for (i = 0; i < pageCount; i++) {
            pageNumber = pageNumbers[i];
            page = me.getPage(pageNumber);
            pageSize = page.length;
            for (j = 0; j < pageSize; j++) {
                if (fn.call(scope, page[j], (pageNumber - 1) * me.pageSize + j) === false) {
                    return;
                }
            }
        }
    },
 
    /**
    * Returns the first record in this page map which elicits a true return value from the
    * passed selection function.
    *
    * **IMPORTANT
    * This can ONLY find records which happen to be cached in the page cache. This will be parts of the dataset around the currently
    * visible zone, or recently visited zones if the pages have not yet been purged from the cache.
    * 
    * This CAN NOT find records which have not been loaded into the cache.**
    *
    * If full client side searching is required, do not use a buffered store, instead use a regular, fully loaded store and
    * use the {@link Ext.grid.plugin.BufferedRenderer BufferedRenderer} plugin to minimize DOM footprint.
    * @param {Function} fn The selection function to execute for each item.
    *  @param {Mixed} fn.rec The record.
    *  @param {Mixed} fn.index The index in the total dataset of the record.
    * @param {Object} [scope] The scope (`this` reference) in which the function is executed. Defaults to this PageMap.
    * @return {Object} The first record in this page map which returned true from the selection
    * function, or null if none was found.
    */
    findBy: function(fn, scope) {
        var me = this,
            result = null;
 
        scope = scope || me;
        me.forEach(function(rec, index) {
            if (fn.call(scope, rec, index)) {
                result = rec;
                return false;
            }
        });
        return result;
    },
 
    /**
    * Returns the index *in the whole dataset* of the first record in this page map which elicits a true return value from the
    * passed selection function.
    *
    * **IMPORTANT
    * This can ONLY find records which happen to be cached in the page cache. This will be parts of the dataset around the currently
    * visible zone, or recently visited zones if the pages have not yet been purged from the cache.
    * 
    * This CAN NOT find records which have not been loaded into the cache.**
    *
    * If full client side searching is required, do not use a buffered store, instead use a regular, fully loaded store and
    * use the {@link Ext.grid.plugin.BufferedRenderer BufferedRenderer} plugin to minimize DOM footprint.
    * @param {Function} fn The selection function to execute for each item.
    *  @param {Mixed} fn.rec The record.
    *  @param {Mixed} fn.index The index in the total dataset of the record.
    * @param {Object} [scope] The scope (`this` reference) in which the function is executed. Defaults to this PageMap.
    * @return {Number} The index first record in this page map which returned true from the selection
    * function, or -1 if none was found.
    */
    findIndexBy: function(fn, scope) {
        var me = this,
            result = -1;
 
        scope = scope || me;
        me.forEach(function(rec, index) {
            if (fn.call(scope, rec)) {
                result = index;
                return false;
            }
        });
        return result;
    },
 
    getPageFromRecordIndex: function() {
        return Ext.data.Store.prototype.getPageFromRecordIndex.apply(this, arguments);
    },
 
    addAll: function(records) {
        //<debug>
        if (this.getCount()) {
            Ext.Error.raise('Cannot addAll to a non-empty PageMap');
        }
        //</debug>
        this.addPage(1, records);
    },
 
    addPage: function(pageNumber, records) {
        var me = this,
            lastPage = pageNumber + Math.floor((records.length - 1) / me.pageSize),
            startIdx,
            page;
 
        // Account for being handed a block of records spanning several pages.
        // This can happen when loading from a MemoryProxy before a viewSize has been determined.
        for (startIdx = 0; pageNumber <= lastPage; pageNumber++, startIdx += me.pageSize) {
            page = Ext.Array.slice(records, startIdx, startIdx + me.pageSize);
            me.add(pageNumber, page);
            me.fireEvent('pageAdded', pageNumber, page);
        }
    },
 
    getCount: function() {
        var result = this.callParent();
        if (result) {
            result = (result - 1) * this.pageSize + this.last.value.length;
        }
        return result;
    },
 
    indexOf: function(record) {
        return record ? record.index : -1;
    },
 
    insert: function() {
        //<debug>
        Ext.Error.raise('insert operation not suppported into buffered Store');
        //</debug>
    },
 
    remove: function() {
        //<debug>
        Ext.Error.raise('remove operation not suppported from buffered Store');
        //</debug>
    },
 
    removeAt: function() {
        //<debug>
        Ext.Error.raise('removeAt operation not suppported from buffered Store');
        //</debug>
    },
 
    getPage: function(pageNumber) {
        return this.get(pageNumber);
    },
 
    hasRange: function(start, end) {
        var pageNumber = this.getPageFromRecordIndex(start),
            endPageNumber = this.getPageFromRecordIndex(end);
 
        for (; pageNumber <= endPageNumber; pageNumber++) {
            if (!this.hasPage(pageNumber)) {
                return false;
            }
        }
        return true;
    },
 
    hasPage: function(pageNumber) {
        // We must use this.get to trigger an access so that the page which is checked for presence is not eligible for pruning
        return !!this.get(pageNumber);
    },
 
    getAt: function(index) {
        return this.getRange(index, index)[0];
    },
 
    getRange: function(start, end) {
        if (!this.hasRange(start, end)) {
            Ext.Error.raise('PageMap asked for range which it does not have');
        }
        var me = this,
            startPageNumber = me.getPageFromRecordIndex(start),
            endPageNumber = me.getPageFromRecordIndex(end),
            dataStart = (startPageNumber - 1) * me.pageSize,
            dataEnd = (endPageNumber * me.pageSize) - 1,
            pageNumber = startPageNumber,
            result = [],
            sliceBegin, sliceEnd, doSlice,
            i = 0, len;
 
        for (; pageNumber <= endPageNumber; pageNumber++) {
 
            // First and last pages will need slicing to cut into the actual wanted records
            if (pageNumber == startPageNumber) {
                sliceBegin = start - dataStart;
                doSlice = true;
            } else {
                sliceBegin = 0;
                doSlice = false;
            }
            if (pageNumber == endPageNumber) {
                sliceEnd = me.pageSize - (dataEnd - end);
                doSlice = true;
            }
 
            // First and last pages will need slicing
            if (doSlice) {
                Ext.Array.push(result, Ext.Array.slice(me.getPage(pageNumber), sliceBegin, sliceEnd));
            } else {
                Ext.Array.push(result, me.getPage(pageNumber));
            }
        }
 
        // Inject the dataset ordinal position into the record as the index
        for (len = result.length; i < len; i++) {
            result[i].index = start++;
        }
        return result;
    }
});