-
- this.sortToggle = {};
- if(this.sortField){
- this.setDefaultSort(this.sortField, this.sortDir);
- }else if(this.sortInfo){
- this.setDefaultSort(this.sortInfo.field, this.sortInfo.direction);
- }
-
- Ext.data.Store.superclass.constructor.call(this);
-
- if(this.id){
- this.storeId = this.id;
- delete this.id;
- }
- if(this.storeId){
- Ext.StoreMgr.register(this);
- }
- if(this.inlineData){
- this.loadData(this.inlineData);
- delete this.inlineData;
- }else if(this.autoLoad){
- this.load.defer(10, this, [
- typeof this.autoLoad == 'object' ?
- this.autoLoad : undefined]);
- }
- // used internally to uniquely identify a batch
- this.batchCounter = 0;
- this.batches = {};
- },
-
- /**
- * builds a DataWriter instance when Store constructor is provided with a writer config-object instead of an instace.
- * @param {Object} config Writer configuration
- * @return {Ext.data.DataWriter}
- * @private
- */
- buildWriter : function(config) {
- var klass = undefined,
- type = (config.format || 'json').toLowerCase();
- switch (type) {
- case 'json':
- klass = Ext.data.JsonWriter;
- break;
- case 'xml':
- klass = Ext.data.XmlWriter;
- break;
- default:
- klass = Ext.data.JsonWriter;
- }
- return new klass(config);
- },
-
- <div id="method-Ext.data.Store-destroy"></div>/**
- * Destroys the store.
- */
- destroy : function(){
- if(!this.isDestroyed){
- if(this.storeId){
- Ext.StoreMgr.unregister(this);
- }
- this.clearData();
- this.data = null;
- Ext.destroy(this.proxy);
- this.reader = this.writer = null;
- this.purgeListeners();
- this.isDestroyed = true;
- }
- },
-
- <div id="method-Ext.data.Store-add"></div>/**
- * Add Records to the Store and fires the {@link #add} event. To add Records
- * to the store from a remote source use <code>{@link #load}({add:true})</code>.
- * See also <code>{@link #recordType}</code> and <code>{@link #insert}</code>.
- * @param {Ext.data.Record[]} records An Array of Ext.data.Record objects
- * to add to the cache. See {@link #recordType}.
- */
- add : function(records){
- records = [].concat(records);
- if(records.length < 1){
- return;
- }
- for(var i = 0, len = records.length; i < len; i++){
- records[i].join(this);
- }
- var index = this.data.length;
- this.data.addAll(records);
- if(this.snapshot){
- this.snapshot.addAll(records);
- }
- this.fireEvent('add', this, records, index);
- },
-
- <div id="method-Ext.data.Store-addSorted"></div>/**
- * (Local sort only) Inserts the passed Record into the Store at the index where it
- * should go based on the current sort information.
- * @param {Ext.data.Record} record
- */
- addSorted : function(record){
- var index = this.findInsertIndex(record);
- this.insert(index, record);
- },
-
- <div id="method-Ext.data.Store-remove"></div>/**
- * Remove Records from the Store and fires the {@link #remove} event.
- * @param {Ext.data.Record/Ext.data.Record[]} record The record object or array of records to remove from the cache.
- */
- remove : function(record){
- if(Ext.isArray(record)){
- Ext.each(record, function(r){
- this.remove(r);
- }, this);
- }
- var index = this.data.indexOf(record);
- if(index > -1){
- record.join(null);
- this.data.removeAt(index);
- }
- if(this.pruneModifiedRecords){
- this.modified.remove(record);
- }
- if(this.snapshot){
- this.snapshot.remove(record);
- }
- if(index > -1){
- this.fireEvent('remove', this, record, index);
- }
- },
-
- <div id="method-Ext.data.Store-removeAt"></div>/**
- * Remove a Record from the Store at the specified index. Fires the {@link #remove} event.
- * @param {Number} index The index of the record to remove.
- */
- removeAt : function(index){
- this.remove(this.getAt(index));
- },
-
- <div id="method-Ext.data.Store-removeAll"></div>/**
- * Remove all Records from the Store and fires the {@link #clear} event.
- * @param {Boolean} silent [false] Defaults to <tt>false</tt>. Set <tt>true</tt> to not fire clear event.
- */
- removeAll : function(silent){
- var items = [];
- this.each(function(rec){
- items.push(rec);
- });
- this.clearData();
- if(this.snapshot){
- this.snapshot.clear();
- }
- if(this.pruneModifiedRecords){
- this.modified = [];
- }
- if (silent !== true) { // <-- prevents write-actions when we just want to clear a store.
- this.fireEvent('clear', this, items);
- }
- },
-
- // private
- onClear: function(store, records){
- Ext.each(records, function(rec, index){
- this.destroyRecord(this, rec, index);
- }, this);
- },
-
- <div id="method-Ext.data.Store-insert"></div>/**
- * Inserts Records into the Store at the given index and fires the {@link #add} event.
- * See also <code>{@link #add}</code> and <code>{@link #addSorted}</code>.
- * @param {Number} index The start index at which to insert the passed Records.
- * @param {Ext.data.Record[]} records An Array of Ext.data.Record objects to add to the cache.
- */
- insert : function(index, records){
- records = [].concat(records);
- for(var i = 0, len = records.length; i < len; i++){
- this.data.insert(index, records[i]);
- records[i].join(this);
- }
- if(this.snapshot){
- this.snapshot.addAll(records);
- }
- this.fireEvent('add', this, records, index);
- },
-
- <div id="method-Ext.data.Store-indexOf"></div>/**
- * Get the index within the cache of the passed Record.
- * @param {Ext.data.Record} record The Ext.data.Record object to find.
- * @return {Number} The index of the passed Record. Returns -1 if not found.
- */
- indexOf : function(record){
- return this.data.indexOf(record);
- },
-
- <div id="method-Ext.data.Store-indexOfId"></div>/**
- * Get the index within the cache of the Record with the passed id.
- * @param {String} id The id of the Record to find.
- * @return {Number} The index of the Record. Returns -1 if not found.
- */
- indexOfId : function(id){
- return this.data.indexOfKey(id);
- },
-
- <div id="method-Ext.data.Store-getById"></div>/**
- * Get the Record with the specified id.
- * @param {String} id The id of the Record to find.
- * @return {Ext.data.Record} The Record with the passed id. Returns undefined if not found.
- */
- getById : function(id){
- return (this.snapshot || this.data).key(id);
- },
-
- <div id="method-Ext.data.Store-getAt"></div>/**
- * Get the Record at the specified index.
- * @param {Number} index The index of the Record to find.
- * @return {Ext.data.Record} The Record at the passed index. Returns undefined if not found.
- */
- getAt : function(index){
- return this.data.itemAt(index);
- },
-
- <div id="method-Ext.data.Store-getRange"></div>/**
- * Returns a range of Records between specified indices.
- * @param {Number} startIndex (optional) The starting index (defaults to 0)
- * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
- * @return {Ext.data.Record[]} An array of Records
- */
- getRange : function(start, end){
- return this.data.getRange(start, end);
- },
-
- // private
- storeOptions : function(o){
- o = Ext.apply({}, o);
- delete o.callback;
- delete o.scope;
- this.lastOptions = o;
- },
-
- // private
- clearData: function(){
- this.data.each(function(rec) {
- rec.join(null);
- });
- this.data.clear();
- },
-
- <div id="method-Ext.data.Store-load"></div>/**
- * <p>Loads the Record cache from the configured <tt>{@link #proxy}</tt> using the configured <tt>{@link #reader}</tt>.</p>
- * <br><p>Notes:</p><div class="mdetail-params"><ul>
- * <li><b><u>Important</u></b>: loading is asynchronous! This call will return before the new data has been
- * loaded. To perform any post-processing where information from the load call is required, specify
- * the <tt>callback</tt> function to be called, or use a {@link Ext.util.Observable#listeners a 'load' event handler}.</li>
- * <li>If using {@link Ext.PagingToolbar remote paging}, the first load call must specify the <tt>start</tt> and <tt>limit</tt>
- * properties in the <code>options.params</code> property to establish the initial position within the
- * dataset, and the number of Records to cache on each read from the Proxy.</li>
- * <li>If using {@link #remoteSort remote sorting}, the configured <code>{@link #sortInfo}</code>
- * will be automatically included with the posted parameters according to the specified
- * <code>{@link #paramNames}</code>.</li>
- * </ul></div>
- * @param {Object} options An object containing properties which control loading options:<ul>
- * <li><b><tt>params</tt></b> :Object<div class="sub-desc"><p>An object containing properties to pass as HTTP
- * parameters to a remote data source. <b>Note</b>: <code>params</code> will override any
- * <code>{@link #baseParams}</code> of the same name.</p>
- * <p>Parameters are encoded as standard HTTP parameters using {@link Ext#urlEncode}.</p></div></li>
- * <li><b>callback</b> : Function<div class="sub-desc"><p>A function to be called after the Records
- * have been loaded. The callback is called after the load event is fired, and is passed the following arguments:<ul>
- * <li>r : Ext.data.Record[] An Array of Records loaded.</li>
- * <li>options : Options object from the load call.</li>
- * <li>success : Boolean success indicator.</li></ul></p></div></li>
- * <li><b>scope</b> : Object<div class="sub-desc"><p>Scope with which to call the callback (defaults
- * to the Store object)</p></div></li>
- * <li><b>add</b> : Boolean<div class="sub-desc"><p>Indicator to append loaded records rather than
- * replace the current cache. <b>Note</b>: see note for <tt>{@link #loadData}</tt></p></div></li>
- * </ul>
- * @return {Boolean} If the <i>developer</i> provided <tt>{@link #beforeload}</tt> event handler returns
- * <tt>false</tt>, the load call will abort and will return <tt>false</tt>; otherwise will return <tt>true</tt>.
- */
- load : function(options) {
- options = options || {};
- this.storeOptions(options);
- if(this.sortInfo && this.remoteSort){
- var pn = this.paramNames;
- options.params = options.params || {};
- options.params[pn.sort] = this.sortInfo.field;
- options.params[pn.dir] = this.sortInfo.direction;
- }
- try {
- return this.execute('read', null, options); // <-- null represents rs. No rs for load actions.
- } catch(e) {
- this.handleException(e);
- return false;
- }
- },
-
- /**
- * updateRecord Should not be used directly. This method will be called automatically if a Writer is set.
- * Listens to 'update' event.
- * @param {Object} store
- * @param {Object} record
- * @param {Object} action
- * @private
- */
- updateRecord : function(store, record, action) {
- if (action == Ext.data.Record.EDIT && this.autoSave === true && (!record.phantom || (record.phantom && record.isValid()))) {
- this.save();
- }
- },
-
- /**
- * Should not be used directly. Store#add will call this automatically if a Writer is set
- * @param {Object} store
- * @param {Object} rs
- * @param {Object} index
- * @private
- */
- createRecords : function(store, rs, index) {
- for (var i = 0, len = rs.length; i < len; i++) {
- if (rs[i].phantom && rs[i].isValid()) {
- rs[i].markDirty(); // <-- Mark new records dirty
- this.modified.push(rs[i]); // <-- add to modified
- }
- }
- if (this.autoSave === true) {
- this.save();
- }
- },
-
- /**
- * Destroys a record or records. Should not be used directly. It's called by Store#remove if a Writer is set.
- * @param {Store} this
- * @param {Ext.data.Record/Ext.data.Record[]}
- * @param {Number} index
- * @private
- */
- destroyRecord : function(store, record, index) {
- if (this.modified.indexOf(record) != -1) { // <-- handled already if @cfg pruneModifiedRecords == true
- this.modified.remove(record);
- }
- if (!record.phantom) {
- this.removed.push(record);
-
- // since the record has already been removed from the store but the server request has not yet been executed,
- // must keep track of the last known index this record existed. If a server error occurs, the record can be
- // put back into the store. @see Store#createCallback where the record is returned when response status === false
- record.lastIndex = index;
-
- if (this.autoSave === true) {
- this.save();
- }
- }
- },
-
- /**
- * This method should generally not be used directly. This method is called internally
- * by {@link #load}, or if a Writer is set will be called automatically when {@link #add},
- * {@link #remove}, or {@link #update} events fire.
- * @param {String} action Action name ('read', 'create', 'update', or 'destroy')
- * @param {Record/Record[]} rs
- * @param {Object} options
- * @throws Error
- * @private
- */
- execute : function(action, rs, options, /* private */ batch) {
- // blow up if action not Ext.data.CREATE, READ, UPDATE, DESTROY
- if (!Ext.data.Api.isAction(action)) {
- throw new Ext.data.Api.Error('execute', action);
- }
- // make sure options has a fresh, new params hash
- options = Ext.applyIf(options||{}, {
- params: {}
- });
- if(batch !== undefined){
- this.addToBatch(batch);
- }
- // have to separate before-events since load has a different signature than create,destroy and save events since load does not
- // include the rs (record resultset) parameter. Capture return values from the beforeaction into doRequest flag.
- var doRequest = true;
-
- if (action === 'read') {
- doRequest = this.fireEvent('beforeload', this, options);
- Ext.applyIf(options.params, this.baseParams);
- }
- else {
- // if Writer is configured as listful, force single-record rs to be [{}] instead of {}
- // TODO Move listful rendering into DataWriter where the @cfg is defined. Should be easy now.
- if (this.writer.listful === true && this.restful !== true) {
- rs = (Ext.isArray(rs)) ? rs : [rs];
- }
- // if rs has just a single record, shift it off so that Writer writes data as '{}' rather than '[{}]'
- else if (Ext.isArray(rs) && rs.length == 1) {
- rs = rs.shift();
- }
- // Write the action to options.params
- if ((doRequest = this.fireEvent('beforewrite', this, action, rs, options)) !== false) {
- this.writer.apply(options.params, this.baseParams, action, rs);
- }
- }
- if (doRequest !== false) {
- // Send request to proxy.
- if (this.writer && this.proxy.url && !this.proxy.restful && !Ext.data.Api.hasUniqueUrl(this.proxy, action)) {
- options.params.xaction = action; // <-- really old, probaby unecessary.
- }
- // Note: Up until this point we've been dealing with 'action' as a key from Ext.data.Api.actions.
- // We'll flip it now and send the value into DataProxy#request, since it's the value which maps to
- // the user's configured DataProxy#api
- // TODO Refactor all Proxies to accept an instance of Ext.data.Request (not yet defined) instead of this looooooong list
- // of params. This method is an artifact from Ext2.
- this.proxy.request(Ext.data.Api.actions[action], rs, options.params, this.reader, this.createCallback(action, rs, batch), this, options);
- }
- return doRequest;
- },
-
- <div id="method-Ext.data.Store-save"></div>/**
- * Saves all pending changes to the store. If the commensurate Ext.data.Api.actions action is not configured, then
- * the configured <code>{@link #url}</code> will be used.
- * <pre>
- * change url
- * --------------- --------------------
- * removed records Ext.data.Api.actions.destroy
- * phantom records Ext.data.Api.actions.create
- * {@link #getModifiedRecords modified records} Ext.data.Api.actions.update
- * </pre>
- * @TODO: Create extensions of Error class and send associated Record with thrown exceptions.
- * e.g.: Ext.data.DataReader.Error or Ext.data.Error or Ext.data.DataProxy.Error, etc.
- * @return {Number} batch Returns a number to uniquely identify the "batch" of saves occurring. -1 will be returned
- * if there are no items to save or the save was cancelled.
- */
- save : function() {
- if (!this.writer) {
- throw new Ext.data.Store.Error('writer-undefined');
- }
-
- var queue = [],
- len,
- trans,
- batch,
- data = {};
- // DESTROY: First check for removed records. Records in this.removed are guaranteed non-phantoms. @see Store#remove
- if(this.removed.length){
- queue.push(['destroy', this.removed]);
- }
-
- // Check for modified records. Use a copy so Store#rejectChanges will work if server returns error.
- var rs = [].concat(this.getModifiedRecords());
- if(rs.length){
- // CREATE: Next check for phantoms within rs. splice-off and execute create.
- var phantoms = [];
- for(var i = rs.length-1; i >= 0; i--){
- if(rs[i].phantom === true){
- var rec = rs.splice(i, 1).shift();
- if(rec.isValid()){
- phantoms.push(rec);