]>
Commit | Line | Data |
---|---|---|
6527f429 DM |
1 | /**\r |
2 | * ProxyStore is a superclass of {@link Ext.data.Store} and {@link Ext.data.BufferedStore}. It's never used directly,\r | |
3 | * but offers a set of methods used by both of those subclasses.\r | |
4 | *\r | |
5 | * We've left it here in the docs for reference purposes, but unless you need to make a whole new type of Store, what\r | |
6 | * you're probably looking for is {@link Ext.data.Store}. If you're still interested, here's a brief description of what\r | |
7 | * ProxyStore is and is not.\r | |
8 | *\r | |
9 | * ProxyStore provides the basic configuration for anything that can be considered a Store. It expects to be\r | |
10 | * given a {@link Ext.data.Model Model} that represents the type of data in the Store. It also expects to be given a\r | |
11 | * {@link Ext.data.proxy.Proxy Proxy} that handles the loading of data into the Store.\r | |
12 | *\r | |
13 | * ProxyStore provides a few helpful methods such as {@link #method-load} and {@link #sync}, which load and save data\r | |
14 | * respectively, passing the requests through the configured {@link #proxy}.\r | |
15 | *\r | |
16 | * Built-in Store subclasses add extra behavior to each of these functions. Note also that each ProxyStore subclass\r | |
17 | * has its own way of storing data - in {@link Ext.data.Store} the data is saved as a flat {@link Ext.util.Collection Collection},\r | |
18 | * whereas in {@link Ext.data.BufferedStore BufferedStore} we use a {@link Ext.data.PageMap} to maintain a client side cache of pages of records.\r | |
19 | *\r | |
20 | * The store provides filtering and sorting support. This sorting/filtering can happen on the client side\r | |
21 | * or can be completed on the server. This is controlled by the {@link Ext.data.Store#remoteSort remoteSort} and\r | |
22 | * {@link Ext.data.Store#remoteFilter remoteFilter} config options. For more information see the {@link #method-sort} and\r | |
23 | * {@link Ext.data.Store#filter filter} methods.\r | |
24 | */\r | |
25 | Ext.define('Ext.data.ProxyStore', {\r | |
26 | extend: 'Ext.data.AbstractStore',\r | |
27 | \r | |
28 | requires: [\r | |
29 | 'Ext.data.Model',\r | |
30 | 'Ext.data.proxy.Proxy',\r | |
31 | 'Ext.data.proxy.Memory',\r | |
32 | 'Ext.data.operation.*'\r | |
33 | ],\r | |
34 | \r | |
35 | config: {\r | |
36 | // @cmd-auto-dependency {aliasPrefix: "model.", mvc: true, blame: "all"}\r | |
37 | /**\r | |
38 | * @cfg {String/Ext.data.Model} model\r | |
39 | * Name of the {@link Ext.data.Model Model} associated with this store. See\r | |
40 | * {@link Ext.data.Model#entityName}.\r | |
41 | *\r | |
42 | * May also be the actual Model subclass.\r | |
43 | *\r | |
44 | * This config is required for the store to be able to read data unless you have defined\r | |
45 | * the {@link #fields} config which will create an anonymous `Ext.data.Model`.\r | |
46 | */\r | |
47 | model: undefined,\r | |
48 | \r | |
49 | // @cmd-auto-dependency {aliasPrefix: "data.field."}\r | |
50 | /**\r | |
51 | * @cfg {Object[]/String[]} fields\r | |
52 | * @inheritdoc Ext.data.Model#cfg-fields\r | |
53 | * \r | |
54 | * @localdoc **Note:** In general, this configuration option should only be used \r | |
55 | * for simple stores like a two-field store of \r | |
56 | * {@link Ext.form.field.ComboBox ComboBox}. For anything more complicated, such \r | |
57 | * as specifying a particular id property or associations, a \r | |
58 | * {@link Ext.data.Model Model} should be defined and specified for the \r | |
59 | * {@link #model} config.\r | |
60 | * \r | |
61 | * @since 2.3.0\r | |
62 | */\r | |
63 | fields: null,\r | |
64 | \r | |
65 | // @cmd-auto-dependency {aliasPrefix : "proxy."}\r | |
66 | /**\r | |
67 | * @cfg {String/Ext.data.proxy.Proxy/Object} proxy\r | |
68 | * The Proxy to use for this Store. This can be either a string, a config object or a Proxy instance -\r | |
69 | * see {@link #setProxy} for details.\r | |
70 | * @since 1.1.0\r | |
71 | */\r | |
72 | proxy: undefined,\r | |
73 | \r | |
74 | /**\r | |
75 | * @cfg {Boolean/Object} autoLoad\r | |
76 | * If data is not specified, and if autoLoad is true or an Object, this store's load method is automatically called\r | |
77 | * after creation. If the value of autoLoad is an Object, this Object will be passed to the store's load method.\r | |
78 | *\r | |
79 | * It's important to note that {@link Ext.data.TreeStore Tree Stores} will \r | |
80 | * load regardless of autoLoad's value if expand is set to true on the \r | |
81 | * {@link Ext.data.TreeStore#root root node}.\r | |
82 | * \r | |
83 | * @since 2.3.0\r | |
84 | */\r | |
85 | autoLoad: undefined,\r | |
86 | \r | |
87 | /**\r | |
88 | * @cfg {Boolean} autoSync\r | |
89 | * True to automatically sync the Store with its Proxy after every edit to one of its Records. Defaults to false.\r | |
90 | */\r | |
91 | autoSync: false,\r | |
92 | \r | |
93 | /**\r | |
94 | * @cfg {String} batchUpdateMode\r | |
95 | * Sets the updating behavior based on batch synchronization. 'operation' (the default) will update the Store's\r | |
96 | * internal representation of the data after each operation of the batch has completed, 'complete' will wait until\r | |
97 | * the entire batch has been completed before updating the Store's data. 'complete' is a good choice for local\r | |
98 | * storage proxies, 'operation' is better for remote proxies, where there is a comparatively high latency.\r | |
99 | */\r | |
100 | batchUpdateMode: 'operation',\r | |
101 | \r | |
102 | /**\r | |
103 | * @cfg {Boolean} sortOnLoad\r | |
104 | * If true, any sorters attached to this Store will be run after loading data, before the datachanged event is fired.\r | |
105 | * Defaults to true, ignored if {@link Ext.data.Store#remoteSort remoteSort} is true\r | |
106 | */\r | |
107 | sortOnLoad: true,\r | |
108 | \r | |
109 | /**\r | |
110 | * @cfg {Boolean} [trackRemoved=true]\r | |
111 | * This config controls whether removed records are remembered by this store for\r | |
112 | * later saving to the server.\r | |
113 | */\r | |
114 | trackRemoved: true,\r | |
115 | \r | |
116 | /**\r | |
117 | * @cfg {Boolean} [asynchronousLoad]\r | |
118 | * This defaults to `true` when this store's {@link #cfg-proxy} is asynchronous, such as an\r | |
119 | * {@link Ext.data.proxy.Ajax Ajax proxy}.\r | |
120 | *\r | |
121 | * When the proxy is synchronous, such as a {@link Ext.data.proxy.Memory} memory proxy, this\r | |
122 | * defaults to `false`.\r | |
123 | *\r | |
124 | * *NOTE:* This does not cause synchronous Ajax requests if configured `false` when an Ajax proxy\r | |
125 | * is used. It causes immediate issuing of an Ajax request when {@link #method-load} is called\r | |
126 | * rather than issuing the request at the end of the current event handler run.\r | |
127 | *\r | |
128 | * What this means is that when using an Ajax proxy, calls to \r | |
129 | * {@link #method-load} do not fire the request to the remote resource \r | |
130 | * immediately, but schedule a request to be made. This is so that multiple \r | |
131 | * requests are not fired when mutating a store's remote filters and sorters (as \r | |
132 | * happens during state restoration). The request is made only once after all \r | |
133 | * relevant store state is fully set.\r | |
134 | *\r | |
135 | * @since 6.0.1\r | |
136 | */\r | |
137 | asynchronousLoad: undefined\r | |
138 | },\r | |
139 | \r | |
140 | onClassExtended: function(cls, data, hooks) {\r | |
141 | var model = data.model,\r | |
142 | onBeforeClassCreated;\r | |
143 | \r | |
144 | if (typeof model === 'string') {\r | |
145 | onBeforeClassCreated = hooks.onBeforeCreated;\r | |
146 | \r | |
147 | hooks.onBeforeCreated = function() {\r | |
148 | var me = this,\r | |
149 | args = arguments;\r | |
150 | \r | |
151 | Ext.require(model, function() {\r | |
152 | onBeforeClassCreated.apply(me, args);\r | |
153 | });\r | |
154 | };\r | |
155 | }\r | |
156 | },\r | |
157 | \r | |
158 | /**\r | |
159 | * @private\r | |
160 | * @property {Boolean}\r | |
161 | * The class name of the model that this store uses if no explicit {@link #model} is given\r | |
162 | */\r | |
163 | implicitModel: 'Ext.data.Model',\r | |
164 | \r | |
165 | /**\r | |
166 | * @property {Object} lastOptions\r | |
167 | * Property to hold the last options from a {@link #method-load} method call. This object is used for the {@link #method-reload}\r | |
168 | * to reuse the same options. Please see {@link #method-reload} for a simple example on how to use the lastOptions property.\r | |
169 | */\r | |
170 | \r | |
171 | /**\r | |
172 | * @property {Number} autoSyncSuspended\r | |
173 | * A counter to track suspensions.\r | |
174 | * @private\r | |
175 | */\r | |
176 | autoSyncSuspended: 0,\r | |
177 | \r | |
178 | //documented above\r | |
179 | constructor: function(config) {\r | |
180 | var me = this;\r | |
181 | \r | |
182 | // <debug>\r | |
183 | var configModel = me.model;\r | |
184 | // </debug>\r | |
185 | \r | |
186 | /**\r | |
187 | * @event beforeload\r | |
188 | * Fires before a request is made for a new data object. If the beforeload handler returns false the load\r | |
189 | * action will be canceled.\r | |
190 | * @param {Ext.data.Store} store This Store\r | |
191 | * @param {Ext.data.operation.Operation} operation The Ext.data.operation.Operation object that will be passed to the Proxy to\r | |
192 | * load the Store\r | |
193 | * @since 1.1.0\r | |
194 | */\r | |
195 | \r | |
196 | /**\r | |
197 | * @event load\r | |
198 | * Fires whenever the store reads data from a remote data source.\r | |
199 | * @param {Ext.data.Store} this\r | |
200 | * @param {Ext.data.Model[]} records An array of records\r | |
201 | * @param {Boolean} successful True if the operation was successful.\r | |
202 | * @param {Ext.data.operation.Read} operation The \r | |
203 | * {@link Ext.data.operation.Read Operation} object that was used in the data \r | |
204 | * load call\r | |
205 | * @since 1.1.0\r | |
206 | */\r | |
207 | \r | |
208 | /**\r | |
209 | * @event write\r | |
210 | * Fires whenever a successful write has been made via the configured {@link #proxy Proxy}\r | |
211 | * @param {Ext.data.Store} store This Store\r | |
212 | * @param {Ext.data.operation.Operation} operation The {@link Ext.data.operation.Operation Operation} object that was used in\r | |
213 | * the write\r | |
214 | * @since 3.4.0\r | |
215 | */\r | |
216 | \r | |
217 | /**\r | |
218 | * @event beforesync\r | |
219 | * Fired before a call to {@link #sync} is executed. Return false from any listener to cancel the sync\r | |
220 | * @param {Object} options Hash of all records to be synchronized, broken down into create, update and destroy\r | |
221 | */\r | |
222 | \r | |
223 | /**\r | |
224 | * @event metachange\r | |
225 | * Fires when this store's underlying reader (available via the proxy) provides new metadata.\r | |
226 | * Metadata usually consists of new field definitions, but can include any configuration data\r | |
227 | * required by an application, and can be processed as needed in the event handler.\r | |
228 | * This event is currently only fired for JsonReaders.\r | |
229 | * @param {Ext.data.Store} this\r | |
230 | * @param {Object} meta The JSON metadata\r | |
231 | * @since 1.1.0\r | |
232 | */\r | |
233 | \r | |
234 | \r | |
235 | /**\r | |
236 | * Temporary cache in which removed model instances are kept until successfully\r | |
237 | * synchronised with a Proxy, at which point this is cleared.\r | |
238 | *\r | |
239 | * This cache is maintained unless you set `trackRemoved` to `false`.\r | |
240 | *\r | |
241 | * @protected\r | |
242 | * @property {Ext.data.Model[]} removed\r | |
243 | */\r | |
244 | me.removed = [];\r | |
245 | \r | |
246 | me.callParent(arguments);\r | |
247 | \r | |
248 | if (me.getAsynchronousLoad() === false) {\r | |
249 | me.flushLoad();\r | |
250 | }\r | |
251 | \r | |
252 | // <debug>\r | |
253 | if (!me.getModel() && me.useModelWarning !== false && me.getStoreId() !== 'ext-empty-store') {\r | |
254 | // There are a number of ways things could have gone wrong, try to give as much information as possible\r | |
255 | var logMsg = [\r | |
256 | Ext.getClassName(me) || 'Store',\r | |
257 | ' created with no model.'\r | |
258 | ];\r | |
259 | \r | |
260 | if (typeof configModel === 'string') {\r | |
261 | logMsg.push(" The name '", configModel, "'", ' does not correspond to a valid model.');\r | |
262 | }\r | |
263 | \r | |
264 | Ext.log.warn(logMsg.join(''));\r | |
265 | }\r | |
266 | // </debug>\r | |
267 | },\r | |
268 | \r | |
269 | applyAsynchronousLoad: function(asynchronousLoad) {\r | |
270 | // Default in an asynchronousLoad setting.\r | |
271 | // It defaults to false if the proxy is synchronous, and true if the proxy is asynchronous.\r | |
272 | if (asynchronousLoad == null) {\r | |
273 | asynchronousLoad = !this.loadsSynchronously();\r | |
274 | }\r | |
275 | return asynchronousLoad;\r | |
276 | },\r | |
277 | \r | |
278 | updateAutoLoad: function(autoLoad) {\r | |
279 | // Ensure the data collection is set up\r | |
280 | this.getData();\r | |
281 | if (autoLoad) {\r | |
282 | // Defer the load until idle, when the store (and probably the view) is fully constructed\r | |
283 | this.load(Ext.isObject(autoLoad) ? autoLoad : undefined);\r | |
284 | }\r | |
285 | },\r | |
286 | \r | |
287 | /**\r | |
288 | * Returns the total number of {@link Ext.data.Model Model} instances that the {@link Ext.data.proxy.Proxy Proxy}\r | |
289 | * indicates exist. This will usually differ from {@link #getCount} when using paging - getCount returns the\r | |
290 | * number of records loaded into the Store at the moment, getTotalCount returns the number of records that\r | |
291 | * could be loaded into the Store if the Store contained all data\r | |
292 | * @return {Number} The total number of Model instances available via the Proxy. 0 returned if\r | |
293 | * no value has been set via the reader.\r | |
294 | */\r | |
295 | getTotalCount: function() {\r | |
296 | return this.totalCount || 0;\r | |
297 | },\r | |
298 | \r | |
299 | applyFields: function(fields) {\r | |
300 | if (fields) {\r | |
301 | this.createImplicitModel(fields);\r | |
302 | }\r | |
303 | },\r | |
304 | \r | |
305 | applyModel: function(model) {\r | |
306 | if (model) {\r | |
307 | model = Ext.data.schema.Schema.lookupEntity(model);\r | |
308 | }\r | |
309 | // If no model, ensure that the fields config is converted to a model.\r | |
310 | else {\r | |
311 | this.getFields();\r | |
312 | model = this.getModel() || this.createImplicitModel();\r | |
313 | }\r | |
314 | return model;\r | |
315 | },\r | |
316 | \r | |
317 | applyProxy: function(proxy) {\r | |
318 | var model = this.getModel();\r | |
319 | \r | |
320 | if (proxy !== null) {\r | |
321 | if (proxy) {\r | |
322 | if (proxy.isProxy) {\r | |
323 | proxy.setModel(model);\r | |
324 | } else {\r | |
325 | if (Ext.isString(proxy)) {\r | |
326 | proxy = {\r | |
327 | type: proxy,\r | |
328 | model: model\r | |
329 | };\r | |
330 | } else if (!proxy.model) {\r | |
331 | proxy = Ext.apply({\r | |
332 | model: model\r | |
333 | }, proxy);\r | |
334 | }\r | |
335 | \r | |
336 | proxy = Ext.createByAlias('proxy.' + proxy.type, proxy);\r | |
337 | proxy.autoCreated = true;\r | |
338 | }\r | |
339 | } else if (model) {\r | |
340 | proxy = model.getProxy();\r | |
341 | }\r | |
342 | \r | |
343 | if (!proxy) {\r | |
344 | proxy = Ext.createByAlias('proxy.memory');\r | |
345 | proxy.autoCreated = true;\r | |
346 | }\r | |
347 | }\r | |
348 | \r | |
349 | return proxy;\r | |
350 | },\r | |
351 | \r | |
352 | applyState: function (state) {\r | |
353 | var me = this;\r | |
354 | \r | |
355 | me.callParent([state]);\r | |
356 | \r | |
357 | // This is called during construction. Sorters and filters might have changed\r | |
358 | // which require a reload.\r | |
359 | // If autoLoad is true, it might have loaded synchronously from a memory proxy, so needs to reload.\r | |
360 | // If it is already loaded, we definitely need to reload to apply the state.\r | |
361 | if (me.getAutoLoad() || me.isLoaded()) {\r | |
362 | me.load();\r | |
363 | }\r | |
364 | },\r | |
365 | \r | |
366 | updateProxy: function(proxy, oldProxy) {\r | |
367 | this.proxyListeners = Ext.destroy(this.proxyListeners);\r | |
368 | },\r | |
369 | \r | |
370 | updateTrackRemoved: function (track) {\r | |
371 | this.cleanRemoved();\r | |
372 | this.removed = track ? [] : null;\r | |
373 | },\r | |
374 | \r | |
375 | /**\r | |
376 | * @private\r | |
377 | */\r | |
378 | onMetaChange: function(proxy, meta) {\r | |
379 | this.fireEvent('metachange', this, meta);\r | |
380 | },\r | |
381 | \r | |
382 | //saves any phantom records\r | |
383 | create: function(data, options) {\r | |
384 | var me = this,\r | |
385 | Model = me.getModel(),\r | |
386 | instance = new Model(data),\r | |
387 | operation;\r | |
388 | \r | |
389 | options = Ext.apply({}, options);\r | |
390 | if (!options.records) {\r | |
391 | options.records = [instance];\r | |
392 | }\r | |
393 | options.internalScope = me;\r | |
394 | options.internalCallback = me.onProxyWrite;\r | |
395 | \r | |
396 | operation = me.createOperation('create', options);\r | |
397 | return operation.execute();\r | |
398 | },\r | |
399 | \r | |
400 | read: function() {\r | |
401 | return this.load.apply(this, arguments);\r | |
402 | },\r | |
403 | \r | |
404 | update: function(options) {\r | |
405 | var me = this,\r | |
406 | operation;\r | |
407 | \r | |
408 | options = Ext.apply({}, options);\r | |
409 | if (!options.records) {\r | |
410 | options.records = me.getUpdatedRecords();\r | |
411 | }\r | |
412 | options.internalScope = me;\r | |
413 | options.internalCallback = me.onProxyWrite;\r | |
414 | \r | |
415 | operation = me.createOperation('update', options);\r | |
416 | return operation.execute();\r | |
417 | },\r | |
418 | \r | |
419 | /**\r | |
420 | * @private\r | |
421 | * Callback for any write Operation over the Proxy. Updates the Store's MixedCollection to reflect\r | |
422 | * the updates provided by the Proxy\r | |
423 | */\r | |
424 | onProxyWrite: function(operation) {\r | |
425 | var me = this,\r | |
426 | success = operation.wasSuccessful(),\r | |
427 | records = operation.getRecords();\r | |
428 | \r | |
429 | switch (operation.getAction()) {\r | |
430 | case 'create':\r | |
431 | me.onCreateRecords(records, operation, success);\r | |
432 | break;\r | |
433 | case 'update':\r | |
434 | me.onUpdateRecords(records, operation, success);\r | |
435 | break;\r | |
436 | case 'destroy':\r | |
437 | me.onDestroyRecords(records, operation, success);\r | |
438 | break;\r | |
439 | }\r | |
440 | \r | |
441 | if (success) {\r | |
442 | me.fireEvent('write', me, operation);\r | |
443 | me.fireEvent('datachanged', me);\r | |
444 | }\r | |
445 | },\r | |
446 | \r | |
447 | // may be implemented by store subclasses\r | |
448 | onCreateRecords: Ext.emptyFn,\r | |
449 | \r | |
450 | // may be implemented by store subclasses\r | |
451 | onUpdateRecords: Ext.emptyFn,\r | |
452 | \r | |
453 | /**\r | |
454 | * Removes any records when a write is returned from the server.\r | |
455 | * @private\r | |
456 | * @param {Ext.data.Model[]} records The array of removed records\r | |
457 | * @param {Ext.data.operation.Operation} operation The operation that just completed\r | |
458 | * @param {Boolean} success True if the operation was successful\r | |
459 | */\r | |
460 | onDestroyRecords: function(records, operation, success) {\r | |
461 | if (success) {\r | |
462 | this.cleanRemoved();\r | |
463 | }\r | |
464 | },\r | |
465 | \r | |
466 | // tells the attached proxy to destroy the given records\r | |
467 | // @since 3.4.0\r | |
468 | erase: function(options) {\r | |
469 | var me = this,\r | |
470 | operation;\r | |
471 | \r | |
472 | options = Ext.apply({}, options);\r | |
473 | if (!options.records) {\r | |
474 | options.records = me.getRemovedRecords();\r | |
475 | }\r | |
476 | options.internalScope = me;\r | |
477 | options.internalCallback = me.onProxyWrite;\r | |
478 | \r | |
479 | operation = me.createOperation('destroy', options);\r | |
480 | return operation.execute();\r | |
481 | },\r | |
482 | \r | |
483 | /**\r | |
484 | * @private\r | |
485 | * Attached as the 'operationcomplete' event listener to a proxy's Batch object. By default just calls through\r | |
486 | * to onProxyWrite.\r | |
487 | */\r | |
488 | onBatchOperationComplete: function(batch, operation) {\r | |
489 | return this.onProxyWrite(operation);\r | |
490 | },\r | |
491 | \r | |
492 | /**\r | |
493 | * @private\r | |
494 | * Attached as the 'complete' event listener to a proxy's Batch object. Iterates over the batch operations\r | |
495 | * and updates the Store's internal data MixedCollection.\r | |
496 | */\r | |
497 | onBatchComplete: function(batch, operation) {\r | |
498 | var me = this,\r | |
499 | operations = batch.operations,\r | |
500 | length = operations.length,\r | |
501 | i;\r | |
502 | \r | |
503 | if (me.batchUpdateMode !== 'operation') {\r | |
504 | me.suspendEvents();\r | |
505 | \r | |
506 | for (i = 0; i < length; i++) {\r | |
507 | me.onProxyWrite(operations[i]);\r | |
508 | }\r | |
509 | \r | |
510 | me.resumeEvents();\r | |
511 | }\r | |
512 | \r | |
513 | me.isSyncing = false;\r | |
514 | me.fireEvent('datachanged', me);\r | |
515 | },\r | |
516 | \r | |
517 | /**\r | |
518 | * @private\r | |
519 | */\r | |
520 | onBatchException: function(batch, operation) {\r | |
521 | // //decide what to do... could continue with the next operation\r | |
522 | // batch.start();\r | |
523 | //\r | |
524 | // //or retry the last operation\r | |
525 | // batch.retry();\r | |
526 | },\r | |
527 | \r | |
528 | /**\r | |
529 | * @private\r | |
530 | * Filter function for new records.\r | |
531 | */\r | |
532 | filterNew: function(item) {\r | |
533 | // only want phantom records that are valid\r | |
534 | return item.phantom === true && item.isValid();\r | |
535 | },\r | |
536 | \r | |
537 | /**\r | |
538 | * Returns all `{@link Ext.data.Model#property-phantom phantom}` records in this store.\r | |
539 | * @return {Ext.data.Model[]} A possibly empty array of `phantom` records.\r | |
540 | */\r | |
541 | getNewRecords: function() {\r | |
542 | return [];\r | |
543 | },\r | |
544 | \r | |
545 | /**\r | |
546 | * Returns all valid, non-phantom Model instances that have been updated in the Store but not yet synchronized with the Proxy.\r | |
547 | * @return {Ext.data.Model[]} The updated Model instances\r | |
548 | */\r | |
549 | getUpdatedRecords: function() {\r | |
550 | return [];\r | |
551 | },\r | |
552 | \r | |
553 | /**\r | |
554 | * Gets all {@link Ext.data.Model records} added or updated since the last commit. Note that the order of records\r | |
555 | * returned is not deterministic and does not indicate the order in which records were modified. Note also that\r | |
556 | * removed records are not included (use {@link #getRemovedRecords} for that).\r | |
557 | * @return {Ext.data.Model[]} The added and updated Model instances\r | |
558 | */\r | |
559 | getModifiedRecords : function(){\r | |
560 | return [].concat(this.getNewRecords(), this.getUpdatedRecords());\r | |
561 | },\r | |
562 | \r | |
563 | /**\r | |
564 | * @private\r | |
565 | * Filter function for updated records.\r | |
566 | */\r | |
567 | filterUpdated: function(item) {\r | |
568 | // only want dirty records, not phantoms that are valid\r | |
569 | return item.dirty === true && item.phantom !== true && item.isValid();\r | |
570 | },\r | |
571 | \r | |
572 | /**\r | |
573 | * Returns any records that have been removed from the store but not yet destroyed on the proxy.\r | |
574 | * @return {Ext.data.Model[]} The removed Model instances. Note that this is a *copy* of the store's\r | |
575 | * array, so may be mutated.\r | |
576 | */\r | |
577 | getRemovedRecords: function() {\r | |
578 | var removed = this.getRawRemovedRecords();\r | |
579 | // If trackRemoved: false, removed will be null\r | |
580 | return removed ? Ext.Array.clone(removed) : removed;\r | |
581 | },\r | |
582 | \r | |
583 | /**\r | |
584 | * Synchronizes the store with its {@link #proxy}. This asks the proxy to batch together any new, updated\r | |
585 | * and deleted records in the store, updating the store's internal representation of the records\r | |
586 | * as each operation completes.\r | |
587 | * \r | |
588 | * @param {Object} [options] Object containing one or more properties supported by the sync method (these get \r | |
589 | * passed along to the underlying proxy's {@link Ext.data.Proxy#batch batch} method):\r | |
590 | * \r | |
591 | * @param {Ext.data.Batch/Object} [options.batch] A {@link Ext.data.Batch} object (or batch config to apply \r | |
592 | * to the created batch). If unspecified a default batch will be auto-created as needed.\r | |
593 | * \r | |
594 | * @param {Function} [options.callback] The function to be called upon completion of the sync.\r | |
595 | * The callback is called regardless of success or failure and is passed the following parameters:\r | |
596 | * @param {Ext.data.Batch} options.callback.batch The {@link Ext.data.Batch batch} that was processed,\r | |
597 | * containing all operations in their current state after processing\r | |
598 | * @param {Object} options.callback.options The options argument that was originally passed into sync\r | |
599 | * \r | |
600 | * @param {Function} [options.success] The function to be called upon successful completion of the sync. The \r | |
601 | * success function is called only if no exceptions were reported in any operations. If one or more exceptions\r | |
602 | * occurred then the failure function will be called instead. The success function is called \r | |
603 | * with the following parameters:\r | |
604 | * @param {Ext.data.Batch} options.success.batch The {@link Ext.data.Batch batch} that was processed,\r | |
605 | * containing all operations in their current state after processing\r | |
606 | * @param {Object} options.success.options The options argument that was originally passed into sync\r | |
607 | * \r | |
608 | * @param {Function} [options.failure] The function to be called upon unsuccessful completion of the sync. The \r | |
609 | * failure function is called when one or more operations returns an exception during processing (even if some\r | |
610 | * operations were also successful). In this case you can check the batch's {@link Ext.data.Batch#exceptions \r | |
611 | * exceptions} array to see exactly which operations had exceptions. The failure function is called with the \r | |
612 | * following parameters:\r | |
613 | * @param {Ext.data.Batch} options.failure.batch The {@link Ext.data.Batch} that was processed, containing all\r | |
614 | * operations in their current state after processing\r | |
615 | * @param {Object} options.failure.options The options argument that was originally passed into sync\r | |
616 | * \r | |
617 | * @param {Object} [options.params] Additional params to send during the sync Operation(s).\r | |
618 | *\r | |
619 | * @param {Object} [options.scope] The scope in which to execute any callbacks (i.e. the `this` object inside\r | |
620 | * the callback, success and/or failure functions). Defaults to the store's proxy.\r | |
621 | * \r | |
622 | * @return {Ext.data.Store} this\r | |
623 | */\r | |
624 | sync: function(options) {\r | |
625 | var me = this,\r | |
626 | operations = {},\r | |
627 | toCreate = me.getNewRecords(),\r | |
628 | toUpdate = me.getUpdatedRecords(),\r | |
629 | toDestroy = me.getRemovedRecords(),\r | |
630 | needsSync = false;\r | |
631 | \r | |
632 | //<debug>\r | |
633 | if (me.isSyncing) {\r | |
634 | Ext.log.warn('Sync called while a sync operation is in progress. Consider configuring autoSync as false.');\r | |
635 | }\r | |
636 | //</debug>\r | |
637 | \r | |
638 | me.needsSync = false;\r | |
639 | \r | |
640 | if (toCreate.length > 0) {\r | |
641 | operations.create = toCreate;\r | |
642 | needsSync = true;\r | |
643 | }\r | |
644 | \r | |
645 | if (toUpdate.length > 0) {\r | |
646 | operations.update = toUpdate;\r | |
647 | needsSync = true;\r | |
648 | }\r | |
649 | \r | |
650 | if (toDestroy.length > 0) {\r | |
651 | operations.destroy = toDestroy;\r | |
652 | needsSync = true;\r | |
653 | }\r | |
654 | \r | |
655 | if (needsSync && me.fireEvent('beforesync', operations) !== false) {\r | |
656 | me.isSyncing = true;\r | |
657 | \r | |
658 | options = options || {};\r | |
659 | \r | |
660 | me.proxy.batch(Ext.apply(options, {\r | |
661 | operations: operations,\r | |
662 | listeners: me.getBatchListeners()\r | |
663 | }));\r | |
664 | }\r | |
665 | \r | |
666 | return me;\r | |
667 | },\r | |
668 | \r | |
669 | /**\r | |
670 | * @private\r | |
671 | * Returns an object which is passed in as the listeners argument to proxy.batch inside this.sync.\r | |
672 | * This is broken out into a separate function to allow for customisation of the listeners\r | |
673 | * @return {Object} The listeners object\r | |
674 | */\r | |
675 | getBatchListeners: function() {\r | |
676 | var me = this,\r | |
677 | listeners = {\r | |
678 | scope: me,\r | |
679 | exception: me.onBatchException,\r | |
680 | complete: me.onBatchComplete\r | |
681 | };\r | |
682 | \r | |
683 | if (me.batchUpdateMode === 'operation') {\r | |
684 | listeners.operationcomplete = me.onBatchOperationComplete;\r | |
685 | }\r | |
686 | \r | |
687 | return listeners;\r | |
688 | },\r | |
689 | \r | |
690 | /**\r | |
691 | * Saves all pending changes via the configured {@link #proxy}. Use {@link #sync} instead.\r | |
692 | * @deprecated 4.0.0 Will be removed in the next major version\r | |
693 | */\r | |
694 | save: function() {\r | |
695 | return this.sync.apply(this, arguments);\r | |
696 | },\r | |
697 | \r | |
698 | /**\r | |
699 | * Marks this store as needing a load. When the current executing event handler exits,\r | |
700 | * this store will send a request to load using its configured {@link #proxy}.\r | |
701 | *\r | |
702 | * Upon return of the data from whatever data source the proxy connected to, the retrieved\r | |
703 | * {@link Ext.data.Model records} will be loaded into this store, and the optional callback will be called.\r | |
704 | * Example usage:\r | |
705 | *\r | |
706 | * store.load({\r | |
707 | * scope: this,\r | |
708 | * callback: function(records, operation, success) {\r | |
709 | * // the {@link Ext.data.operation.Operation operation} object\r | |
710 | * // contains all of the details of the load operation\r | |
711 | * console.log(records);\r | |
712 | * }\r | |
713 | * });\r | |
714 | *\r | |
715 | * If the callback scope does not need to be set, a function can simply be passed:\r | |
716 | *\r | |
717 | * store.load(function(records, operation, success) {\r | |
718 | * console.log('loaded records');\r | |
719 | * });\r | |
720 | *\r | |
721 | * @param {Object} [options] This is passed into the {@link Ext.data.operation.Operation Operation}\r | |
722 | * object that is created and then sent to the proxy's {@link Ext.data.proxy.Proxy#read} function.\r | |
723 | * In addition to the options listed below, this object may contain properties to configure the\r | |
724 | * {@link Ext.data.operation.Operation Operation}.\r | |
725 | * @param {Function} [options.callback] A function which is called when the response arrives.\r | |
726 | * @param {Ext.data.Model[]} options.callback.records Array of records.\r | |
727 | * @param {Ext.data.operation.Operation} options.callback.operation The Operation itself.\r | |
728 | * @param {Boolean} options.callback.success `true` when operation completed successfully.\r | |
729 | * @param {Boolean} [options.addRecords=false] Specify as `true` to *add* the incoming records rather than the\r | |
730 | * default which is to have the incoming records *replace* the existing stoire contents.\r | |
731 | * \r | |
732 | * @return {Ext.data.Store} this\r | |
733 | * @since 1.1.0\r | |
734 | */\r | |
735 | load: function(options) {\r | |
736 | var me = this;\r | |
737 | \r | |
738 | // Legacy option. Specifying a function was allowed.\r | |
739 | if (typeof options === 'function') {\r | |
740 | options = {\r | |
741 | callback: options\r | |
742 | };\r | |
743 | } else {\r | |
744 | // We may mutate the options object in setLoadOptions.\r | |
745 | options = options ? Ext.Object.chain(options) : {};\r | |
746 | }\r | |
747 | \r | |
748 | me.pendingLoadOptions = options;\r | |
749 | \r | |
750 | // If we are configured to load asynchronously (the default for async proxies)\r | |
751 | // then schedule a flush, unless one is already scheduled.\r | |
752 | if (me.getAsynchronousLoad()) {\r | |
753 | if (!me.loadTimer) {\r | |
754 | me.loadTimer = Ext.asap(me.flushLoad, me);\r | |
755 | }\r | |
756 | }\r | |
757 | // If we are configured to load synchronously (the default for sync proxies)\r | |
758 | // then flush the load now.\r | |
759 | else {\r | |
760 | me.flushLoad();\r | |
761 | }\r | |
762 | return me;\r | |
763 | },\r | |
764 | \r | |
765 | /**\r | |
766 | * Called when the event handler which called the {@link #method-load} method exits.\r | |
767 | */\r | |
768 | flushLoad: function() {\r | |
769 | var me = this,\r | |
770 | options = me.pendingLoadOptions,\r | |
771 | operation;\r | |
772 | \r | |
773 | // If it gets called programatically before the timer fired, the listener will need cancelling.\r | |
774 | me.clearLoadTask();\r | |
775 | if (!options) {\r | |
776 | return;\r | |
777 | }\r | |
778 | \r | |
779 | me.setLoadOptions(options);\r | |
780 | \r | |
781 | if (me.getRemoteSort() && options.sorters) {\r | |
782 | me.fireEvent('beforesort', me, options.sorters);\r | |
783 | }\r | |
784 | \r | |
785 | operation = Ext.apply({\r | |
786 | internalScope: me,\r | |
787 | internalCallback: me.onProxyLoad,\r | |
788 | scope: me\r | |
789 | }, options);\r | |
790 | me.lastOptions = operation;\r | |
791 | \r | |
792 | \r | |
793 | operation = me.createOperation('read', operation);\r | |
794 | \r | |
795 | if (me.fireEvent('beforeload', me, operation) !== false) {\r | |
796 | me.onBeforeLoad(operation);\r | |
797 | me.loading = true;\r | |
798 | operation.execute();\r | |
799 | }\r | |
800 | },\r | |
801 | \r | |
802 | /**\r | |
803 | * Reloads the store using the last options passed to the {@link #method-load} method. You can use the reload method to reload the\r | |
804 | * store using the parameters from the last load() call. For example:\r | |
805 | *\r | |
806 | * store.load({\r | |
807 | * params : {\r | |
808 | * userid : 22216\r | |
809 | * }\r | |
810 | * });\r | |
811 | *\r | |
812 | * //...\r | |
813 | *\r | |
814 | * store.reload();\r | |
815 | *\r | |
816 | * The initial {@link #method-load} execution will pass the `userid` parameter in the request. The {@link #reload} execution\r | |
817 | * will also send the same `userid` parameter in its request as it will reuse the `params` object from the last {@link #method-load} call.\r | |
818 | *\r | |
819 | * You can override a param by passing in the config object with the `params` object:\r | |
820 | *\r | |
821 | * store.load({\r | |
822 | * params : {\r | |
823 | * userid : 22216,\r | |
824 | * foo : 'bar'\r | |
825 | * }\r | |
826 | * });\r | |
827 | *\r | |
828 | * //...\r | |
829 | *\r | |
830 | * store.reload({\r | |
831 | * params : {\r | |
832 | * userid : 1234\r | |
833 | * }\r | |
834 | * });\r | |
835 | *\r | |
836 | * The initial {@link #method-load} execution sends the `userid` and `foo` parameters but in the {@link #reload} it only sends\r | |
837 | * the `userid` paramter because you are overriding the `params` config not just overriding the one param. To only change a single param\r | |
838 | * but keep other params, you will have to get the last params from the {@link #lastOptions} property:\r | |
839 | *\r | |
840 | * var lastOptions = store.lastOptions,\r | |
841 | * lastParams = Ext.clone(lastOptions.params); // make a copy of the last params so we don't affect future reload() calls\r | |
842 | *\r | |
843 | * lastParams.userid = 1234;\r | |
844 | *\r | |
845 | * store.reload({\r | |
846 | * params : lastParams\r | |
847 | * });\r | |
848 | *\r | |
849 | * This will now send the `userid` parameter as `1234` and the `foo` param as `'bar'`.\r | |
850 | *\r | |
851 | * @param {Object} [options] A config object which contains options which may override the options passed to the previous load call. See the\r | |
852 | * {@link #method-load} method for valid configs.\r | |
853 | */\r | |
854 | reload: function(options) {\r | |
855 | var o = Ext.apply({}, options, this.lastOptions);\r | |
856 | return this.load(o);\r | |
857 | },\r | |
858 | \r | |
859 | onEndUpdate: function() {\r | |
860 | var me = this;\r | |
861 | \r | |
862 | if (me.needsSync && me.autoSync && !me.autoSyncSuspended) {\r | |
863 | me.sync();\r | |
864 | }\r | |
865 | },\r | |
866 | \r | |
867 | /**\r | |
868 | * @private\r | |
869 | * A model instance should call this method on the Store it has been {@link Ext.data.Model#join joined} to..\r | |
870 | * @param {Ext.data.Model} record The model instance that was edited\r | |
871 | * @since 3.4.0\r | |
872 | */\r | |
873 | afterReject: function(record) {\r | |
874 | var me = this;\r | |
875 | // Must pass the 5th param (modifiedFieldNames) as null, otherwise the\r | |
876 | // event firing machinery appends the listeners "options" object to the arg list\r | |
877 | // which may get used as the modified fields array by a handler.\r | |
878 | // This array is used for selective grid cell updating by Grid View.\r | |
879 | // Null will be treated as though all cells need updating.\r | |
880 | if (me.contains(record)) {\r | |
881 | me.onUpdate(record, Ext.data.Model.REJECT, null);\r | |
882 | me.fireEvent('update', me, record, Ext.data.Model.REJECT, null);\r | |
883 | }\r | |
884 | },\r | |
885 | \r | |
886 | /**\r | |
887 | * @private\r | |
888 | * A model instance should call this method on the Store it has been {@link Ext.data.Model#join joined} to.\r | |
889 | * @param {Ext.data.Model} record The model instance that was edited\r | |
890 | * @since 3.4.0\r | |
891 | */\r | |
892 | afterCommit: function(record, modifiedFieldNames) {\r | |
893 | var me = this;\r | |
894 | if (!modifiedFieldNames) {\r | |
895 | modifiedFieldNames = null;\r | |
896 | }\r | |
897 | if (me.contains(record)) {\r | |
898 | me.onUpdate(record, Ext.data.Model.COMMIT, modifiedFieldNames);\r | |
899 | me.fireEvent('update', me, record, Ext.data.Model.COMMIT, modifiedFieldNames);\r | |
900 | }\r | |
901 | },\r | |
902 | \r | |
903 | afterErase: function(record) {\r | |
904 | this.onErase(record);\r | |
905 | },\r | |
906 | \r | |
907 | onErase: Ext.emptyFn,\r | |
908 | \r | |
909 | onUpdate: Ext.emptyFn,\r | |
910 | \r | |
911 | /**\r | |
912 | * @private\r | |
913 | */\r | |
914 | onDestroy: function() {\r | |
915 | var me = this,\r | |
916 | proxy = me.getProxy();\r | |
917 | \r | |
918 | me.clearLoadTask();\r | |
919 | me.getData().destroy();\r | |
920 | me.data = null;\r | |
921 | me.setProxy(null);\r | |
922 | \r | |
923 | if (proxy.autoCreated) {\r | |
924 | proxy.destroy();\r | |
925 | }\r | |
926 | \r | |
927 | me.setModel(null);\r | |
928 | },\r | |
929 | \r | |
930 | \r | |
931 | /**\r | |
932 | * Returns true if the store has a pending load task.\r | |
933 | * @return {Boolean} `true` if the store has a pending load task.\r | |
934 | * @private\r | |
935 | */\r | |
936 | hasPendingLoad: function() {\r | |
937 | return !!this.pendingLoadOptions || this.isLoading();\r | |
938 | },\r | |
939 | \r | |
940 | /**\r | |
941 | * Returns true if the Store is currently performing a load operation\r | |
942 | * @return {Boolean} `true` if the Store is currently loading\r | |
943 | */\r | |
944 | isLoading: function() {\r | |
945 | return !!this.loading;\r | |
946 | },\r | |
947 | \r | |
948 | /**\r | |
949 | * Returns `true` if the Store has been loaded.\r | |
950 | * @return {Boolean} `true` if the Store has been loaded.\r | |
951 | */\r | |
952 | isLoaded: function() {\r | |
953 | return this.loadCount > 0;\r | |
954 | },\r | |
955 | \r | |
956 | /**\r | |
957 | * Suspends automatically syncing the Store with its Proxy. Only applicable if {@link #autoSync} is `true`\r | |
958 | */\r | |
959 | suspendAutoSync: function() {\r | |
960 | ++this.autoSyncSuspended;\r | |
961 | },\r | |
962 | \r | |
963 | /**\r | |
964 | * Resumes automatically syncing the Store with its Proxy. Only applicable if {@link #autoSync} is `true`\r | |
965 | * @param {Boolean} syncNow Pass `true` to synchronize now. Only synchronizes with the Proxy if the suspension\r | |
966 | * count has gone to zero (We are not under a higher level of suspension)\r | |
967 | * \r | |
968 | */\r | |
969 | resumeAutoSync: function(syncNow) {\r | |
970 | var me = this;\r | |
971 | \r | |
972 | //<debug>\r | |
973 | if (!me.autoSyncSuspended) {\r | |
974 | Ext.log.warn('Mismatched call to resumeAutoSync - auto synchronization is currently not suspended.');\r | |
975 | }\r | |
976 | //</debug>\r | |
977 | if (me.autoSyncSuspended && ! --me.autoSyncSuspended) {\r | |
978 | if (syncNow) {\r | |
979 | me.sync();\r | |
980 | }\r | |
981 | }\r | |
982 | },\r | |
983 | \r | |
984 | /**\r | |
985 | * Removes all records from the store. This method does a "fast remove",\r | |
986 | * individual remove events are not called. The {@link #clear} event is\r | |
987 | * fired upon completion.\r | |
988 | * @method\r | |
989 | * @since 1.1.0\r | |
990 | */\r | |
991 | removeAll: Ext.emptyFn,\r | |
992 | // individual store subclasses should implement a "fast" remove\r | |
993 | // and fire a clear event afterwards\r | |
994 | \r | |
995 | // to be implemented by subclasses\r | |
996 | clearData: Ext.emptyFn,\r | |
997 | \r | |
998 | privates: {\r | |
999 | /**\r | |
1000 | * @private\r | |
1001 | * Returns the array of records which have been removed since the last time this store was synced.\r | |
1002 | *\r | |
1003 | * This is used internally, when purging removed records after a successful sync.\r | |
1004 | * This is overridden by TreeStore because TreeStore accumulates deleted records on removal\r | |
1005 | * of child nodes from their parent, *not* on removal of records from its collection. The collection\r | |
1006 | * has records added on expand, and removed on collapse.\r | |
1007 | */\r | |
1008 | getRawRemovedRecords: function() {\r | |
1009 | return this.removed;\r | |
1010 | },\r | |
1011 | \r | |
1012 | onExtraParamsChanged: function() {\r | |
1013 | \r | |
1014 | },\r | |
1015 | \r | |
1016 | clearLoadTask: function() {\r | |
1017 | Ext.asapCancel(this.loadTimer);\r | |
1018 | this.pendingLoadOptions = this.loadTimer = null;\r | |
1019 | },\r | |
1020 | \r | |
1021 | cleanRemoved: function() {\r | |
1022 | // Must use class-specific getRawRemovedRecords.\r | |
1023 | // Regular Stores add to the "removed" property on remove.\r | |
1024 | // TreeStores are having records removed all the time; node collapse removes.\r | |
1025 | // TreeStores add to the "removedNodes" property onNodeRemove\r | |
1026 | var removed = this.getRawRemovedRecords(),\r | |
1027 | len, i;\r | |
1028 | \r | |
1029 | if (removed) {\r | |
1030 | for (i = 0, len = removed.length; i < len; ++i) {\r | |
1031 | removed[i].unjoin(this);\r | |
1032 | }\r | |
1033 | removed.length = 0;\r | |
1034 | }\r | |
1035 | },\r | |
1036 | \r | |
1037 | createOperation: function(type, options) {\r | |
1038 | var me = this,\r | |
1039 | proxy = me.getProxy(),\r | |
1040 | listeners;\r | |
1041 | \r | |
1042 | if (!me.proxyListeners) {\r | |
1043 | listeners = {\r | |
1044 | scope: me,\r | |
1045 | destroyable: true,\r | |
1046 | beginprocessresponse: me.beginUpdate,\r | |
1047 | endprocessresponse: me.endUpdate\r | |
1048 | };\r | |
1049 | \r | |
1050 | if (!me.disableMetaChangeEvent) {\r | |
1051 | listeners.metachange = me.onMetaChange;\r | |
1052 | }\r | |
1053 | me.proxyListeners = proxy.on(listeners);\r | |
1054 | }\r | |
1055 | return proxy.createOperation(type, options);\r | |
1056 | },\r | |
1057 | \r | |
1058 | createImplicitModel: function(fields) {\r | |
1059 | var me = this,\r | |
1060 | modelCfg = {\r | |
1061 | extend: me.implicitModel,\r | |
1062 | statics: {\r | |
1063 | defaultProxy: 'memory'\r | |
1064 | }\r | |
1065 | },\r | |
1066 | proxy, model;\r | |
1067 | \r | |
1068 | if (fields) {\r | |
1069 | modelCfg.fields = fields;\r | |
1070 | }\r | |
1071 | model = Ext.define(null, modelCfg);\r | |
1072 | \r | |
1073 | me.setModel(model);\r | |
1074 | \r | |
1075 | proxy = me.getProxy();\r | |
1076 | if (proxy) {\r | |
1077 | model.setProxy(proxy);\r | |
1078 | } else {\r | |
1079 | me.setProxy(model.getProxy());\r | |
1080 | }\r | |
1081 | },\r | |
1082 | \r | |
1083 | loadsSynchronously: function() {\r | |
1084 | return this.getProxy().isSynchronous;\r | |
1085 | },\r | |
1086 | \r | |
1087 | onBeforeLoad: Ext.privateFn,\r | |
1088 | \r | |
1089 | removeFromRemoved: function(record) {\r | |
1090 | // Must use class-specific getRawRemovedRecords.\r | |
1091 | // Regular Stores add to the "removed" property on remove.\r | |
1092 | // TreeStores are having records removed all the time; node collapse removes.\r | |
1093 | // TreeStores add to the "removedNodes" property onNodeRemove\r | |
1094 | var removed = this.getRawRemovedRecords();\r | |
1095 | if (removed) {\r | |
1096 | Ext.Array.remove(removed, record);\r | |
1097 | record.unjoin(this);\r | |
1098 | }\r | |
1099 | },\r | |
1100 | \r | |
1101 | setLoadOptions: function(options) {\r | |
1102 | var me = this,\r | |
1103 | filters, sorters;\r | |
1104 | \r | |
1105 | if (me.getRemoteFilter()) {\r | |
1106 | filters = me.getFilters(false);\r | |
1107 | if (filters && filters.getCount()) {\r | |
1108 | options.filters = filters.getRange();\r | |
1109 | }\r | |
1110 | }\r | |
1111 | \r | |
1112 | if (me.getRemoteSort()) {\r | |
1113 | sorters = me.getSorters(false);\r | |
1114 | if (sorters && sorters.getCount()) {\r | |
1115 | options.sorters = sorters.getRange();\r | |
1116 | }\r | |
1117 | }\r | |
1118 | }\r | |
1119 | }\r | |
1120 | \r | |
1121 | });\r |