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; }
11 <script type="text/javascript">
12 function highlight() {
13 document.getElementById(location.hash.replace(/#/, "")).className = "highlight";
17 <body onload="prettyPrint(); highlight();">
18 <pre class="prettyprint lang-js"><span id='Ext-data-proxy-WebStorage'>/**
19 </span> * @author Ed Spencer
20 * @class Ext.data.proxy.WebStorage
21 * @extends Ext.data.proxy.Client
23 * <p>WebStorageProxy is simply a superclass for the {@link Ext.data.proxy.LocalStorage localStorage} and
24 * {@link Ext.data.proxy.SessionStorage sessionStorage} proxies. It uses the new HTML5 key/value client-side storage
25 * objects to save {@link Ext.data.Model model instances} for offline use.</p>
27 Ext.define('Ext.data.proxy.WebStorage', {
28 extend: 'Ext.data.proxy.Client',
29 alternateClassName: 'Ext.data.WebStorageProxy',
31 <span id='Ext-data-proxy-WebStorage-cfg-id'> /**
32 </span> * @cfg {String} id The unique ID used as the key in which all record data are stored in the local storage object
36 <span id='Ext-data-proxy-WebStorage-method-constructor'> /**
37 </span> * Creates the proxy, throws an error if local storage is not supported in the current browser
38 * @param {Object} config (optional) Config object.
40 constructor: function(config) {
41 this.callParent(arguments);
43 <span id='Ext-data-proxy-WebStorage-property-cache'> /**
44 </span> * Cached map of records already retrieved by this Proxy - ensures that the same instance is always retrieved
51 if (this.getStorageObject() === undefined) {
52 Ext.Error.raise("Local Storage is not supported in this browser, please use another type of data proxy");
56 //if an id is not given, try to use the store's id instead
57 this.id = this.id || (this.store ? this.store.storeId : undefined);
60 if (this.id === undefined) {
61 Ext.Error.raise("No unique id was provided to the local storage proxy. See Ext.data.proxy.LocalStorage documentation for details");
69 create: function(operation, callback, scope) {
70 var records = operation.records,
71 length = records.length,
75 operation.setStarted();
77 for (i = 0; i < length; i++) {
81 record.phantom = false;
82 id = this.getNextId();
87 this.setRecord(record, id);
93 operation.setCompleted();
94 operation.setSuccessful();
96 if (typeof callback == 'function') {
97 callback.call(scope || this, operation);
102 read: function(operation, callback, scope) {
103 //TODO: respect sorters, filters, start and limit options on the Operation
108 i, recordData, record;
110 //read a single record
112 record = this.getRecord(operation.id);
115 records.push(record);
116 operation.setSuccessful();
119 for (i = 0; i < length; i++) {
120 records.push(this.getRecord(ids[i]));
122 operation.setSuccessful();
125 operation.setCompleted();
127 operation.resultSet = Ext.create('Ext.data.ResultSet', {
129 total : records.length,
133 if (typeof callback == 'function') {
134 callback.call(scope || this, operation);
139 update: function(operation, callback, scope) {
140 var records = operation.records,
141 length = records.length,
145 operation.setStarted();
147 for (i = 0; i < length; i++) {
149 this.setRecord(record);
151 //we need to update the set of ids here because it's possible that a non-phantom record was added
152 //to this proxy - in which case the record's id would never have been added via the normal 'create' call
154 if (id !== undefined && Ext.Array.indexOf(ids, id) == -1) {
160 operation.setCompleted();
161 operation.setSuccessful();
163 if (typeof callback == 'function') {
164 callback.call(scope || this, operation);
169 destroy: function(operation, callback, scope) {
170 var records = operation.records,
171 length = records.length,
174 //newIds is a copy of ids, from which we remove the destroyed records
175 newIds = [].concat(ids),
178 for (i = 0; i < length; i++) {
179 Ext.Array.remove(newIds, records[i].getId());
180 this.removeRecord(records[i], false);
185 operation.setCompleted();
186 operation.setSuccessful();
188 if (typeof callback == 'function') {
189 callback.call(scope || this, operation);
193 <span id='Ext-data-proxy-WebStorage-method-getRecord'> /**
195 * Fetches a model instance from the Proxy by ID. Runs each field's decode function (if present) to decode the data
196 * @param {String} id The record's unique ID
197 * @return {Ext.data.Model} The model instance
199 getRecord: function(id) {
200 if (this.cache[id] === undefined) {
201 var rawData = Ext.decode(this.getStorageObject().getItem(this.getRecordKey(id))),
204 fields = Model.prototype.fields.items,
205 length = fields.length,
206 i, field, name, record;
208 for (i = 0; i < length; i++) {
212 if (typeof field.decode == 'function') {
213 data[name] = field.decode(rawData[name]);
215 data[name] = rawData[name];
219 record = new Model(data, id);
220 record.phantom = false;
222 this.cache[id] = record;
225 return this.cache[id];
228 <span id='Ext-data-proxy-WebStorage-method-setRecord'> /**
229 </span> * Saves the given record in the Proxy. Runs each field's encode function (if present) to encode the data
230 * @param {Ext.data.Model} record The model instance
231 * @param {String} id The id to save the record under (defaults to the value of the record's getId() function)
233 setRecord: function(record, id) {
241 rawData = record.data,
244 fields = model.prototype.fields.items,
245 length = fields.length,
247 field, name, obj, key;
249 for (; i < length; i++) {
253 if (typeof field.encode == 'function') {
254 data[name] = field.encode(rawData[name], record);
256 data[name] = rawData[name];
260 obj = me.getStorageObject();
261 key = me.getRecordKey(id);
263 //keep the cache up to date
264 me.cache[id] = record;
266 //iPad bug requires that we remove the item before setting it
268 obj.setItem(key, Ext.encode(data));
271 <span id='Ext-data-proxy-WebStorage-method-removeRecord'> /**
273 * Physically removes a given record from the local storage. Used internally by {@link #destroy}, which you should
274 * use instead because it updates the list of currently-stored record ids
275 * @param {String|Number|Ext.data.Model} id The id of the record to remove, or an Ext.data.Model instance
277 removeRecord: function(id, updateIds) {
285 if (updateIds !== false) {
287 Ext.Array.remove(ids, id);
291 me.getStorageObject().removeItem(me.getRecordKey(id));
294 <span id='Ext-data-proxy-WebStorage-method-getRecordKey'> /**
296 * Given the id of a record, returns a unique string based on that id and the id of this proxy. This is used when
297 * storing data in the local storage object and should prevent naming collisions.
298 * @param {String|Number|Ext.data.Model} id The record id, or a Model instance
299 * @return {String} The unique key for this record
301 getRecordKey: function(id) {
306 return Ext.String.format("{0}-{1}", this.id, id);
309 <span id='Ext-data-proxy-WebStorage-method-getRecordCounterKey'> /**
311 * Returns the unique key used to store the current record counter for this proxy. This is used internally when
312 * realizing models (creating them when they used to be phantoms), in order to give each model instance a unique id.
313 * @return {String} The counter key
315 getRecordCounterKey: function() {
316 return Ext.String.format("{0}-counter", this.id);
319 <span id='Ext-data-proxy-WebStorage-method-getIds'> /**
321 * Returns the array of record IDs stored in this Proxy
322 * @return {Array} The record IDs. Each is cast as a Number
325 var ids = (this.getStorageObject().getItem(this.id) || "").split(","),
329 if (length == 1 && ids[0] === "") {
332 for (i = 0; i < length; i++) {
333 ids[i] = parseInt(ids[i], 10);
340 <span id='Ext-data-proxy-WebStorage-method-setIds'> /**
342 * Saves the array of ids representing the set of all records in the Proxy
343 * @param {Array} ids The ids to set
345 setIds: function(ids) {
346 var obj = this.getStorageObject(),
347 str = ids.join(",");
349 obj.removeItem(this.id);
351 if (!Ext.isEmpty(str)) {
352 obj.setItem(this.id, str);
356 <span id='Ext-data-proxy-WebStorage-method-getNextId'> /**
358 * Returns the next numerical ID that can be used when realizing a model instance (see getRecordCounterKey). Increments
360 * @return {Number} The id
362 getNextId: function() {
363 var obj = this.getStorageObject(),
364 key = this.getRecordCounterKey(),
365 last = obj.getItem(key),
370 last = ids[ids.length - 1] || 0;
373 id = parseInt(last, 10) + 1;
374 obj.setItem(key, id);
379 <span id='Ext-data-proxy-WebStorage-method-initialize'> /**
381 * Sets up the Proxy by claiming the key in the storage object that corresponds to the unique id of this Proxy. Called
382 * automatically by the constructor, this should not need to be called again unless {@link #clear} has been called.
384 initialize: function() {
385 var storageObject = this.getStorageObject();
386 storageObject.setItem(this.id, storageObject.getItem(this.id) || "");
389 <span id='Ext-data-proxy-WebStorage-method-clear'> /**
390 </span> * Destroys all records stored in the proxy and removes all keys and values used to support the proxy from the storage object
393 var obj = this.getStorageObject(),
398 //remove all the records
399 for (i = 0; i < len; i++) {
400 this.removeRecord(ids[i]);
403 //remove the supporting objects
404 obj.removeItem(this.getRecordCounterKey());
405 obj.removeItem(this.id);
408 <span id='Ext-data-proxy-WebStorage-method-getStorageObject'> /**
410 * Abstract function which should return the storage object that data will be saved to. This must be implemented
412 * @return {Object} The storage object
414 getStorageObject: function() {
416 Ext.Error.raise("The getStorageObject function has not been defined in your Ext.data.proxy.WebStorage subclass");