Upgrade to ExtJS 4.0.1 - Released 05/18/2011
[extjs.git] / docs / source / AbstractStore.html
1 <!DOCTYPE html>
2 <html>
3 <head>
4   <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
5   <title>The source code</title>
6   <link href="../prettify/prettify.css" type="text/css" rel="stylesheet" />
7   <script type="text/javascript" src="../prettify/prettify.js"></script>
8   <style type="text/css">
9     .highlight { display: block; background-color: #ddd; }
10   </style>
11   <script type="text/javascript">
12     function highlight() {
13       document.getElementById(location.hash.replace(/#/, "")).className = "highlight";
14     }
15   </script>
16 </head>
17 <body onload="prettyPrint(); highlight();">
18   <pre class="prettyprint lang-js"><span id='Ext-data-AbstractStore'>/**
19 </span> * @author Ed Spencer
20  * @class Ext.data.AbstractStore
21  *
22  * &lt;p&gt;AbstractStore is a superclass of {@link Ext.data.Store} and {@link Ext.data.TreeStore}. It's never used directly,
23  * but offers a set of methods used by both of those subclasses.&lt;/p&gt;
24  * 
25  * &lt;p&gt;We've left it here in the docs for reference purposes, but unless you need to make a whole new type of Store, what
26  * you're probably looking for is {@link Ext.data.Store}. If you're still interested, here's a brief description of what 
27  * AbstractStore is and is not.&lt;/p&gt;
28  * 
29  * &lt;p&gt;AbstractStore provides the basic configuration for anything that can be considered a Store. It expects to be 
30  * given a {@link Ext.data.Model Model} that represents the type of data in the Store. It also expects to be given a 
31  * {@link Ext.data.proxy.Proxy Proxy} that handles the loading of data into the Store.&lt;/p&gt;
32  * 
33  * &lt;p&gt;AbstractStore provides a few helpful methods such as {@link #load} and {@link #sync}, which load and save data
34  * respectively, passing the requests through the configured {@link #proxy}. Both built-in Store subclasses add extra
35  * behavior to each of these functions. Note also that each AbstractStore subclass has its own way of storing data - 
36  * in {@link Ext.data.Store} the data is saved as a flat {@link Ext.util.MixedCollection MixedCollection}, whereas in
37  * {@link Ext.data.TreeStore TreeStore} we use a {@link Ext.data.Tree} to maintain the data's hierarchy.&lt;/p&gt;
38  * 
39  * TODO: Update these docs to explain about the sortable and filterable mixins.
40  * &lt;p&gt;Finally, AbstractStore provides an API for sorting and filtering data via its {@link #sorters} and {@link #filters}
41  * {@link Ext.util.MixedCollection MixedCollections}. Although this functionality is provided by AbstractStore, there's a
42  * good description of how to use it in the introduction of {@link Ext.data.Store}.
43  * 
44  */
45 Ext.define('Ext.data.AbstractStore', {
46     requires: ['Ext.util.MixedCollection', 'Ext.data.Operation', 'Ext.util.Filter'],
47     
48     mixins: {
49         observable: 'Ext.util.Observable',
50         sortable: 'Ext.util.Sortable'
51     },
52     
53     statics: {
54         create: function(store){
55             if (!store.isStore) {
56                 if (!store.type) {
57                     store.type = 'store';
58                 }
59                 store = Ext.createByAlias('store.' + store.type, store);
60             }
61             return store;
62         }    
63     },
64     
65     remoteSort  : false,
66     remoteFilter: false,
67
68 <span id='Ext-data-AbstractStore-cfg-proxy'>    /**
69 </span>     * @cfg {String/Ext.data.proxy.Proxy/Object} proxy The Proxy to use for this Store. This can be either a string, a config
70      * object or a Proxy instance - see {@link #setProxy} for details.
71      */
72
73 <span id='Ext-data-AbstractStore-cfg-autoLoad'>    /**
74 </span>     * @cfg {Boolean/Object} autoLoad If data is not specified, and if autoLoad is true or an Object, this store's load method
75      * is automatically called after creation. If the value of autoLoad is an Object, this Object will be passed to the store's
76      * load method. Defaults to false.
77      */
78     autoLoad: false,
79
80 <span id='Ext-data-AbstractStore-cfg-autoSync'>    /**
81 </span>     * @cfg {Boolean} autoSync True to automatically sync the Store with its Proxy after every edit to one of its Records.
82      * Defaults to false.
83      */
84     autoSync: false,
85
86 <span id='Ext-data-AbstractStore-property-batchUpdateMode'>    /**
87 </span>     * Sets the updating behavior based on batch synchronization. 'operation' (the default) will update the Store's
88      * internal representation of the data after each operation of the batch has completed, 'complete' will wait until
89      * the entire batch has been completed before updating the Store's data. 'complete' is a good choice for local
90      * storage proxies, 'operation' is better for remote proxies, where there is a comparatively high latency.
91      * @property batchUpdateMode
92      * @type String
93      */
94     batchUpdateMode: 'operation',
95
96 <span id='Ext-data-AbstractStore-property-filterOnLoad'>    /**
97 </span>     * If true, any filters attached to this Store will be run after loading data, before the datachanged event is fired.
98      * Defaults to true, ignored if {@link #remoteFilter} is true
99      * @property filterOnLoad
100      * @type Boolean
101      */
102     filterOnLoad: true,
103
104 <span id='Ext-data-AbstractStore-property-sortOnLoad'>    /**
105 </span>     * If true, any sorters attached to this Store will be run after loading data, before the datachanged event is fired.
106      * Defaults to true, igored if {@link #remoteSort} is true
107      * @property sortOnLoad
108      * @type Boolean
109      */
110     sortOnLoad: true,
111
112 <span id='Ext-data-AbstractStore-property-implicitModel'>    /**
113 </span>     * True if a model was created implicitly for this Store. This happens if a fields array is passed to the Store's constructor
114      * instead of a model constructor or name.
115      * @property implicitModel
116      * @type Boolean
117      * @private
118      */
119     implicitModel: false,
120
121 <span id='Ext-data-AbstractStore-property-defaultProxyType'>    /**
122 </span>     * The string type of the Proxy to create if none is specified. This defaults to creating a {@link Ext.data.proxy.Memory memory proxy}.
123      * @property defaultProxyType
124      * @type String
125      */
126     defaultProxyType: 'memory',
127
128 <span id='Ext-data-AbstractStore-property-isDestroyed'>    /**
129 </span>     * True if the Store has already been destroyed via {@link #destroyStore}. If this is true, the reference to Store should be deleted
130      * as it will not function correctly any more.
131      * @property isDestroyed
132      * @type Boolean
133      */
134     isDestroyed: false,
135
136     isStore: true,
137
138 <span id='Ext-data-AbstractStore-cfg-storeId'>    /**
139 </span>     * @cfg {String} storeId Optional unique identifier for this store. If present, this Store will be registered with 
140      * the {@link Ext.data.StoreManager}, making it easy to reuse elsewhere. Defaults to undefined.
141      */
142     
143 <span id='Ext-data-AbstractStore-cfg-fields'>    /**
144 </span>     * @cfg {Array} fields
145      * This may be used in place of specifying a {@link #model} configuration. The fields should be a 
146      * set of {@link Ext.data.Field} configuration objects. The store will automatically create a {@link Ext.data.Model}
147      * with these fields. In general this configuration option should be avoided, it exists for the purposes of
148      * backwards compatibility. For anything more complicated, such as specifying a particular id property or
149      * assocations, a {@link Ext.data.Model} should be defined and specified for the {@link #model} config.
150      */
151
152     sortRoot: 'data',
153     
154     //documented above
155     constructor: function(config) {
156         var me = this,
157             filters;
158         
159         me.addEvents(
160 <span id='Ext-data-AbstractStore-event-add'>            /**
161 </span>             * @event add
162              * Fired when a Model instance has been added to this Store
163              * @param {Ext.data.Store} store The store
164              * @param {Array} records The Model instances that were added
165              * @param {Number} index The index at which the instances were inserted
166              */
167             'add',
168
169 <span id='Ext-data-AbstractStore-event-remove'>            /**
170 </span>             * @event remove
171              * Fired when a Model instance has been removed from this Store
172              * @param {Ext.data.Store} store The Store object
173              * @param {Ext.data.Model} record The record that was removed
174              * @param {Number} index The index of the record that was removed
175              */
176             'remove',
177             
178 <span id='Ext-data-AbstractStore-event-update'>            /**
179 </span>             * @event update
180              * Fires when a Record has been updated
181              * @param {Store} this
182              * @param {Ext.data.Model} record The Model instance that was updated
183              * @param {String} operation The update operation being performed. Value may be one of:
184              * &lt;pre&gt;&lt;code&gt;
185                Ext.data.Model.EDIT
186                Ext.data.Model.REJECT
187                Ext.data.Model.COMMIT
188              * &lt;/code&gt;&lt;/pre&gt;
189              */
190             'update',
191
192 <span id='Ext-data-AbstractStore-event-datachanged'>            /**
193 </span>             * @event datachanged
194              * Fires whenever the records in the Store have changed in some way - this could include adding or removing records,
195              * or updating the data in existing records
196              * @param {Ext.data.Store} this The data store
197              */
198             'datachanged',
199
200 <span id='Ext-data-AbstractStore-event-beforeload'>            /**
201 </span>             * @event beforeload
202              * Event description
203              * @param {Ext.data.Store} store This Store
204              * @param {Ext.data.Operation} operation The Ext.data.Operation object that will be passed to the Proxy to load the Store
205              */
206             'beforeload',
207
208 <span id='Ext-data-AbstractStore-event-load'>            /**
209 </span>             * @event load
210              * Fires whenever the store reads data from a remote data source.
211              * @param {Ext.data.Store} this
212              * @param {Array} records An array of records
213              * @param {Boolean} successful True if the operation was successful.
214              */
215             'load',
216
217 <span id='Ext-data-AbstractStore-event-beforesync'>            /**
218 </span>             * @event beforesync
219              * Called before a call to {@link #sync} is executed. Return false from any listener to cancel the synv
220              * @param {Object} options Hash of all records to be synchronized, broken down into create, update and destroy
221              */
222             'beforesync',
223 <span id='Ext-data-AbstractStore-event-clear'>            /**
224 </span>             * @event clear
225              * Fired after the {@link #removeAll} method is called.
226              * @param {Ext.data.Store} this
227              */
228             'clear'
229         );
230         
231         Ext.apply(me, config);
232         // don't use *config* anymore from here on... use *me* instead...
233
234 <span id='Ext-data-AbstractStore-property-removed'>        /**
235 </span>         * Temporary cache in which removed model instances are kept until successfully synchronised with a Proxy,
236          * at which point this is cleared.
237          * @private
238          * @property removed
239          * @type Array
240          */
241         me.removed = [];
242
243         me.mixins.observable.constructor.apply(me, arguments);
244         me.model = Ext.ModelManager.getModel(me.model);
245
246 <span id='Ext-data-AbstractStore-property-modelDefaults'>        /**
247 </span>         * @property modelDefaults
248          * @type Object
249          * @private
250          * A set of default values to be applied to every model instance added via {@link #insert} or created via {@link #create}.
251          * This is used internally by associations to set foreign keys and other fields. See the Association classes source code
252          * for examples. This should not need to be used by application developers.
253          */
254         Ext.applyIf(me, {
255             modelDefaults: {}
256         });
257
258         //Supports the 3.x style of simply passing an array of fields to the store, implicitly creating a model
259         if (!me.model &amp;&amp; me.fields) {
260             me.model = Ext.define('Ext.data.Store.ImplicitModel-' + (me.storeId || Ext.id()), {
261                 extend: 'Ext.data.Model',
262                 fields: me.fields,
263                 proxy: me.proxy || me.defaultProxyType
264             });
265
266             delete me.fields;
267
268             me.implicitModel = true;
269         }
270
271         //ensures that the Proxy is instantiated correctly
272         me.setProxy(me.proxy || me.model.getProxy());
273
274         if (me.id &amp;&amp; !me.storeId) {
275             me.storeId = me.id;
276             delete me.id;
277         }
278
279         if (me.storeId) {
280             Ext.data.StoreManager.register(me);
281         }
282         
283         me.mixins.sortable.initSortable.call(me);        
284         
285 <span id='Ext-data-AbstractStore-property-filters'>        /**
286 </span>         * The collection of {@link Ext.util.Filter Filters} currently applied to this Store
287          * @property filters
288          * @type Ext.util.MixedCollection
289          */
290         filters = me.decodeFilters(me.filters);
291         me.filters = Ext.create('Ext.util.MixedCollection');
292         me.filters.addAll(filters);
293     },
294
295 <span id='Ext-data-AbstractStore-method-setProxy'>    /**
296 </span>     * Sets the Store's Proxy by string, config object or Proxy instance
297      * @param {String|Object|Ext.data.proxy.Proxy} proxy The new Proxy, which can be either a type string, a configuration object
298      * or an Ext.data.proxy.Proxy instance
299      * @return {Ext.data.proxy.Proxy} The attached Proxy object
300      */
301     setProxy: function(proxy) {
302         var me = this;
303         
304         if (proxy instanceof Ext.data.proxy.Proxy) {
305             proxy.setModel(me.model);
306         } else {
307             if (Ext.isString(proxy)) {
308                 proxy = {
309                     type: proxy    
310                 };
311             }
312             Ext.applyIf(proxy, {
313                 model: me.model
314             });
315             
316             proxy = Ext.createByAlias('proxy.' + proxy.type, proxy);
317         }
318         
319         me.proxy = proxy;
320         
321         return me.proxy;
322     },
323
324 <span id='Ext-data-AbstractStore-method-getProxy'>    /**
325 </span>     * Returns the proxy currently attached to this proxy instance
326      * @return {Ext.data.proxy.Proxy} The Proxy instance
327      */
328     getProxy: function() {
329         return this.proxy;
330     },
331
332     //saves any phantom records
333     create: function(data, options) {
334         var me = this,
335             instance = Ext.ModelManager.create(Ext.applyIf(data, me.modelDefaults), me.model.modelName),
336             operation;
337         
338         options = options || {};
339
340         Ext.applyIf(options, {
341             action : 'create',
342             records: [instance]
343         });
344
345         operation = Ext.create('Ext.data.Operation', options);
346
347         me.proxy.create(operation, me.onProxyWrite, me);
348         
349         return instance;
350     },
351
352     read: function() {
353         return this.load.apply(this, arguments);
354     },
355
356     onProxyRead: Ext.emptyFn,
357
358     update: function(options) {
359         var me = this,
360             operation;
361         options = options || {};
362
363         Ext.applyIf(options, {
364             action : 'update',
365             records: me.getUpdatedRecords()
366         });
367
368         operation = Ext.create('Ext.data.Operation', options);
369
370         return me.proxy.update(operation, me.onProxyWrite, me);
371     },
372
373 <span id='Ext-data-AbstractStore-method-onProxyWrite'>    /**
374 </span>     * @private
375      * Callback for any write Operation over the Proxy. Updates the Store's MixedCollection to reflect
376      * the updates provided by the Proxy
377      */
378     onProxyWrite: function(operation) {
379         var me = this,
380             success = operation.wasSuccessful(),
381             records = operation.getRecords();
382
383         switch (operation.action) {
384             case 'create':
385                 me.onCreateRecords(records, operation, success);
386                 break;
387             case 'update':
388                 me.onUpdateRecords(records, operation, success);
389                 break;
390             case 'destroy':
391                 me.onDestroyRecords(records, operation, success);
392                 break;
393         }
394
395         if (success) {
396             me.fireEvent('write', me, operation);
397             me.fireEvent('datachanged', me);
398         }
399         //this is a callback that would have been passed to the 'create', 'update' or 'destroy' function and is optional
400         Ext.callback(operation.callback, operation.scope || me, [records, operation, success]);
401     },
402
403
404     //tells the attached proxy to destroy the given records
405     destroy: function(options) {
406         var me = this,
407             operation;
408             
409         options = options || {};
410
411         Ext.applyIf(options, {
412             action : 'destroy',
413             records: me.getRemovedRecords()
414         });
415
416         operation = Ext.create('Ext.data.Operation', options);
417
418         return me.proxy.destroy(operation, me.onProxyWrite, me);
419     },
420
421 <span id='Ext-data-AbstractStore-method-onBatchOperationComplete'>    /**
422 </span>     * @private
423      * Attached as the 'operationcomplete' event listener to a proxy's Batch object. By default just calls through
424      * to onProxyWrite.
425      */
426     onBatchOperationComplete: function(batch, operation) {
427         return this.onProxyWrite(operation);
428     },
429
430 <span id='Ext-data-AbstractStore-method-onBatchComplete'>    /**
431 </span>     * @private
432      * Attached as the 'complete' event listener to a proxy's Batch object. Iterates over the batch operations
433      * and updates the Store's internal data MixedCollection.
434      */
435     onBatchComplete: function(batch, operation) {
436         var me = this,
437             operations = batch.operations,
438             length = operations.length,
439             i;
440
441         me.suspendEvents();
442
443         for (i = 0; i &lt; length; i++) {
444             me.onProxyWrite(operations[i]);
445         }
446
447         me.resumeEvents();
448
449         me.fireEvent('datachanged', me);
450     },
451
452     onBatchException: function(batch, operation) {
453         // //decide what to do... could continue with the next operation
454         // batch.start();
455         //
456         // //or retry the last operation
457         // batch.retry();
458     },
459
460 <span id='Ext-data-AbstractStore-method-filterNew'>    /**
461 </span>     * @private
462      * Filter function for new records.
463      */
464     filterNew: function(item) {
465         // only want phantom records that are valid
466         return item.phantom === true &amp;&amp; item.isValid();
467     },
468
469 <span id='Ext-data-AbstractStore-method-getNewRecords'>    /**
470 </span>     * Returns all Model instances that are either currently a phantom (e.g. have no id), or have an ID but have not
471      * yet been saved on this Store (this happens when adding a non-phantom record from another Store into this one)
472      * @return {Array} The Model instances
473      */
474     getNewRecords: function() {
475         return [];
476     },
477
478 <span id='Ext-data-AbstractStore-method-getUpdatedRecords'>    /**
479 </span>     * Returns all Model instances that have been updated in the Store but not yet synchronized with the Proxy
480      * @return {Array} The updated Model instances
481      */
482     getUpdatedRecords: function() {
483         return [];
484     },
485
486 <span id='Ext-data-AbstractStore-method-filterUpdated'>    /**
487 </span>     * @private
488      * Filter function for updated records.
489      */
490     filterUpdated: function(item) {
491         // only want dirty records, not phantoms that are valid
492         return item.dirty === true &amp;&amp; item.phantom !== true &amp;&amp; item.isValid();
493     },
494
495 <span id='Ext-data-AbstractStore-method-getRemovedRecords'>    /**
496 </span>     * Returns any records that have been removed from the store but not yet destroyed on the proxy.
497      * @return {Array} The removed Model instances
498      */
499     getRemovedRecords: function() {
500         return this.removed;
501     },
502
503     filter: function(filters, value) {
504
505     },
506
507 <span id='Ext-data-AbstractStore-method-decodeFilters'>    /**
508 </span>     * @private
509      * Normalizes an array of filter objects, ensuring that they are all Ext.util.Filter instances
510      * @param {Array} filters The filters array
511      * @return {Array} Array of Ext.util.Filter objects
512      */
513     decodeFilters: function(filters) {
514         if (!Ext.isArray(filters)) {
515             if (filters === undefined) {
516                 filters = [];
517             } else {
518                 filters = [filters];
519             }
520         }
521
522         var length = filters.length,
523             Filter = Ext.util.Filter,
524             config, i;
525
526         for (i = 0; i &lt; length; i++) {
527             config = filters[i];
528
529             if (!(config instanceof Filter)) {
530                 Ext.apply(config, {
531                     root: 'data'
532                 });
533
534                 //support for 3.x style filters where a function can be defined as 'fn'
535                 if (config.fn) {
536                     config.filterFn = config.fn;
537                 }
538
539                 //support a function to be passed as a filter definition
540                 if (typeof config == 'function') {
541                     config = {
542                         filterFn: config
543                     };
544                 }
545
546                 filters[i] = new Filter(config);
547             }
548         }
549
550         return filters;
551     },
552
553     clearFilter: function(supressEvent) {
554
555     },
556
557     isFiltered: function() {
558
559     },
560
561     filterBy: function(fn, scope) {
562
563     },
564     
565 <span id='Ext-data-AbstractStore-method-sync'>    /**
566 </span>     * Synchronizes the Store with its Proxy. This asks the Proxy to batch together any new, updated
567      * and deleted records in the store, updating the Store's internal representation of the records
568      * as each operation completes.
569      */
570     sync: function() {
571         var me        = this,
572             options   = {},
573             toCreate  = me.getNewRecords(),
574             toUpdate  = me.getUpdatedRecords(),
575             toDestroy = me.getRemovedRecords(),
576             needsSync = false;
577
578         if (toCreate.length &gt; 0) {
579             options.create = toCreate;
580             needsSync = true;
581         }
582
583         if (toUpdate.length &gt; 0) {
584             options.update = toUpdate;
585             needsSync = true;
586         }
587
588         if (toDestroy.length &gt; 0) {
589             options.destroy = toDestroy;
590             needsSync = true;
591         }
592
593         if (needsSync &amp;&amp; me.fireEvent('beforesync', options) !== false) {
594             me.proxy.batch(options, me.getBatchListeners());
595         }
596     },
597
598
599 <span id='Ext-data-AbstractStore-method-getBatchListeners'>    /**
600 </span>     * @private
601      * Returns an object which is passed in as the listeners argument to proxy.batch inside this.sync.
602      * This is broken out into a separate function to allow for customisation of the listeners
603      * @return {Object} The listeners object
604      */
605     getBatchListeners: function() {
606         var me = this,
607             listeners = {
608                 scope: me,
609                 exception: me.onBatchException
610             };
611
612         if (me.batchUpdateMode == 'operation') {
613             listeners.operationcomplete = me.onBatchOperationComplete;
614         } else {
615             listeners.complete = me.onBatchComplete;
616         }
617
618         return listeners;
619     },
620
621     //deprecated, will be removed in 5.0
622     save: function() {
623         return this.sync.apply(this, arguments);
624     },
625
626 <span id='Ext-data-AbstractStore-method-load'>    /**
627 </span>     * Loads the Store using its configured {@link #proxy}.
628      * @param {Object} options Optional config object. This is passed into the {@link Ext.data.Operation Operation}
629      * object that is created and then sent to the proxy's {@link Ext.data.proxy.Proxy#read} function
630      */
631     load: function(options) {
632         var me = this,
633             operation;
634
635         options = options || {};
636
637         Ext.applyIf(options, {
638             action : 'read',
639             filters: me.filters.items,
640             sorters: me.getSorters()
641         });
642         
643         operation = Ext.create('Ext.data.Operation', options);
644
645         if (me.fireEvent('beforeload', me, operation) !== false) {
646             me.loading = true;
647             me.proxy.read(operation, me.onProxyLoad, me);
648         }
649         
650         return me;
651     },
652
653 <span id='Ext-data-AbstractStore-method-afterEdit'>    /**
654 </span>     * @private
655      * A model instance should call this method on the Store it has been {@link Ext.data.Model#join joined} to.
656      * @param {Ext.data.Model} record The model instance that was edited
657      */
658     afterEdit : function(record) {
659         var me = this;
660         
661         if (me.autoSync) {
662             me.sync();
663         }
664         
665         me.fireEvent('update', me, record, Ext.data.Model.EDIT);
666     },
667
668 <span id='Ext-data-AbstractStore-method-afterReject'>    /**
669 </span>     * @private
670      * A model instance should call this method on the Store it has been {@link Ext.data.Model#join joined} to..
671      * @param {Ext.data.Model} record The model instance that was edited
672      */
673     afterReject : function(record) {
674         this.fireEvent('update', this, record, Ext.data.Model.REJECT);
675     },
676
677 <span id='Ext-data-AbstractStore-method-afterCommit'>    /**
678 </span>     * @private
679      * A model instance should call this method on the Store it has been {@link Ext.data.Model#join joined} to.
680      * @param {Ext.data.Model} record The model instance that was edited
681      */
682     afterCommit : function(record) {
683         this.fireEvent('update', this, record, Ext.data.Model.COMMIT);
684     },
685
686     clearData: Ext.emptyFn,
687
688     destroyStore: function() {
689         var me = this;
690         
691         if (!me.isDestroyed) {
692             if (me.storeId) {
693                 Ext.data.StoreManager.unregister(me);
694             }
695             me.clearData();
696             me.data = null;
697             me.tree = null;
698             // Ext.destroy(this.proxy);
699             me.reader = me.writer = null;
700             me.clearListeners();
701             me.isDestroyed = true;
702
703             if (me.implicitModel) {
704                 Ext.destroy(me.model);
705             }
706         }
707     },
708     
709     doSort: function(sorterFn) {
710         var me = this;
711         if (me.remoteSort) {
712             //the load function will pick up the new sorters and request the sorted data from the proxy
713             me.load();
714         } else {
715             me.data.sortBy(sorterFn);
716             me.fireEvent('datachanged', me);
717         }
718     },
719
720     getCount: Ext.emptyFn,
721
722     getById: Ext.emptyFn,
723     
724 <span id='Ext-data-AbstractStore-method-removeAll'>    /**
725 </span>     * Removes all records from the store. This method does a &quot;fast remove&quot;,
726      * individual remove events are not called. The {@link #clear} event is
727      * fired upon completion.
728      * @method
729      */
730     removeAll: Ext.emptyFn,
731     // individual substores should implement a &quot;fast&quot; remove
732     // and fire a clear event afterwards
733
734 <span id='Ext-data-AbstractStore-method-isLoading'>    /**
735 </span>     * Returns true if the Store is currently performing a load operation
736      * @return {Boolean} True if the Store is currently loading
737      */
738     isLoading: function() {
739         return this.loading;
740      }
741 });
742 </pre>
743 </body>
744 </html>