4 <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
5 <title>The source code</title>
6 <link href="../resources/prettify/prettify.css" type="text/css" rel="stylesheet" />
7 <script type="text/javascript" src="../resources/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
21 * WebStorageProxy is simply a superclass for the {@link Ext.data.proxy.LocalStorage LocalStorage} and {@link
22 * Ext.data.proxy.SessionStorage SessionStorage} proxies. It uses the new HTML5 key/value client-side storage objects to
23 * save {@link Ext.data.Model model instances} for offline use.
26 Ext.define('Ext.data.proxy.WebStorage', {
27 extend: 'Ext.data.proxy.Client',
28 alternateClassName: 'Ext.data.WebStorageProxy',
30 <span id='Ext-data-proxy-WebStorage-cfg-id'> /**
31 </span> * @cfg {String} id
32 * 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> * @property {Object} cache
45 * Cached map of records already retrieved by this Proxy. Ensures that the same instance is always retrieved.
50 if (this.getStorageObject() === undefined) {
51 Ext.Error.raise("Local Storage is not supported in this browser, please use another type of data proxy");
55 //if an id is not given, try to use the store's id instead
56 this.id = this.id || (this.store ? this.store.storeId : undefined);
59 if (this.id === undefined) {
60 Ext.Error.raise("No unique id was provided to the local storage proxy. See Ext.data.proxy.LocalStorage documentation for details");
68 create: function(operation, callback, scope) {
69 var records = operation.records,
70 length = records.length,
74 operation.setStarted();
76 for (i = 0; i < length; i++) {
80 record.phantom = false;
81 id = this.getNextId();
86 this.setRecord(record, id);
92 operation.setCompleted();
93 operation.setSuccessful();
95 if (typeof callback == 'function') {
96 callback.call(scope || this, operation);
101 read: function(operation, callback, scope) {
102 //TODO: respect sorters, filters, start and limit options on the Operation
107 i, recordData, record;
109 //read a single record
111 record = this.getRecord(operation.id);
114 records.push(record);
115 operation.setSuccessful();
118 for (i = 0; i < length; i++) {
119 records.push(this.getRecord(ids[i]));
121 operation.setSuccessful();
124 operation.setCompleted();
126 operation.resultSet = Ext.create('Ext.data.ResultSet', {
128 total : records.length,
132 if (typeof callback == 'function') {
133 callback.call(scope || this, operation);
138 update: function(operation, callback, scope) {
139 var records = operation.records,
140 length = records.length,
144 operation.setStarted();
146 for (i = 0; i < length; i++) {
148 this.setRecord(record);
150 //we need to update the set of ids here because it's possible that a non-phantom record was added
151 //to this proxy - in which case the record's id would never have been added via the normal 'create' call
153 if (id !== undefined && Ext.Array.indexOf(ids, id) == -1) {
159 operation.setCompleted();
160 operation.setSuccessful();
162 if (typeof callback == 'function') {
163 callback.call(scope || this, operation);
168 destroy: function(operation, callback, scope) {
169 var records = operation.records,
170 length = records.length,
173 //newIds is a copy of ids, from which we remove the destroyed records
174 newIds = [].concat(ids),
177 for (i = 0; i < length; i++) {
178 Ext.Array.remove(newIds, records[i].getId());
179 this.removeRecord(records[i], false);
184 operation.setCompleted();
185 operation.setSuccessful();
187 if (typeof callback == 'function') {
188 callback.call(scope || this, operation);
192 <span id='Ext-data-proxy-WebStorage-method-getRecord'> /**
194 * Fetches a model instance from the Proxy by ID. Runs each field's decode function (if present) to decode the data.
195 * @param {String} id The record's unique ID
196 * @return {Ext.data.Model} The model instance
198 getRecord: function(id) {
199 if (this.cache[id] === undefined) {
200 var rawData = Ext.decode(this.getStorageObject().getItem(this.getRecordKey(id))),
203 fields = Model.prototype.fields.items,
204 length = fields.length,
205 i, field, name, record;
207 for (i = 0; i < length; i++) {
211 if (typeof field.decode == 'function') {
212 data[name] = field.decode(rawData[name]);
214 data[name] = rawData[name];
218 record = new Model(data, id);
219 record.phantom = false;
221 this.cache[id] = record;
224 return this.cache[id];
227 <span id='Ext-data-proxy-WebStorage-method-setRecord'> /**
228 </span> * Saves the given record in the Proxy. Runs each field's encode function (if present) to encode the data.
229 * @param {Ext.data.Model} record The model instance
230 * @param {String} [id] The id to save the record under (defaults to the value of the record's getId() function)
232 setRecord: function(record, id) {
240 rawData = record.data,
243 fields = model.prototype.fields.items,
244 length = fields.length,
246 field, name, obj, key;
248 for (; i < length; i++) {
252 if (typeof field.encode == 'function') {
253 data[name] = field.encode(rawData[name], record);
255 data[name] = rawData[name];
259 obj = me.getStorageObject();
260 key = me.getRecordKey(id);
262 //keep the cache up to date
263 me.cache[id] = record;
265 //iPad bug requires that we remove the item before setting it
267 obj.setItem(key, Ext.encode(data));
270 <span id='Ext-data-proxy-WebStorage-method-removeRecord'> /**
272 * Physically removes a given record from the local storage. Used internally by {@link #destroy}, which you should
273 * use instead because it updates the list of currently-stored record ids
274 * @param {String/Number/Ext.data.Model} id The id of the record to remove, or an Ext.data.Model instance
276 removeRecord: function(id, updateIds) {
284 if (updateIds !== false) {
286 Ext.Array.remove(ids, id);
290 me.getStorageObject().removeItem(me.getRecordKey(id));
293 <span id='Ext-data-proxy-WebStorage-method-getRecordKey'> /**
295 * Given the id of a record, returns a unique string based on that id and the id of this proxy. This is used when
296 * storing data in the local storage object and should prevent naming collisions.
297 * @param {String/Number/Ext.data.Model} id The record id, or a Model instance
298 * @return {String} The unique key for this record
300 getRecordKey: function(id) {
305 return Ext.String.format("{0}-{1}", this.id, id);
308 <span id='Ext-data-proxy-WebStorage-method-getRecordCounterKey'> /**
310 * Returns the unique key used to store the current record counter for this proxy. This is used internally when
311 * realizing models (creating them when they used to be phantoms), in order to give each model instance a unique id.
312 * @return {String} The counter key
314 getRecordCounterKey: function() {
315 return Ext.String.format("{0}-counter", this.id);
318 <span id='Ext-data-proxy-WebStorage-method-getIds'> /**
320 * Returns the array of record IDs stored in this Proxy
321 * @return {Number[]} The record IDs. Each is cast as a Number
324 var ids = (this.getStorageObject().getItem(this.id) || "").split(","),
328 if (length == 1 && ids[0] === "") {
331 for (i = 0; i < length; i++) {
332 ids[i] = parseInt(ids[i], 10);
339 <span id='Ext-data-proxy-WebStorage-method-setIds'> /**
341 * Saves the array of ids representing the set of all records in the Proxy
342 * @param {Number[]} ids The ids to set
344 setIds: function(ids) {
345 var obj = this.getStorageObject(),
346 str = ids.join(",");
348 obj.removeItem(this.id);
350 if (!Ext.isEmpty(str)) {
351 obj.setItem(this.id, str);
355 <span id='Ext-data-proxy-WebStorage-method-getNextId'> /**
357 * Returns the next numerical ID that can be used when realizing a model instance (see getRecordCounterKey).
358 * Increments the counter.
359 * @return {Number} The id
361 getNextId: function() {
362 var obj = this.getStorageObject(),
363 key = this.getRecordCounterKey(),
364 last = obj.getItem(key),
369 last = ids[ids.length - 1] || 0;
372 id = parseInt(last, 10) + 1;
373 obj.setItem(key, id);
378 <span id='Ext-data-proxy-WebStorage-method-initialize'> /**
380 * Sets up the Proxy by claiming the key in the storage object that corresponds to the unique id of this Proxy. Called
381 * automatically by the constructor, this should not need to be called again unless {@link #clear} has been called.
383 initialize: function() {
384 var storageObject = this.getStorageObject();
385 storageObject.setItem(this.id, storageObject.getItem(this.id) || "");
388 <span id='Ext-data-proxy-WebStorage-method-clear'> /**
389 </span> * Destroys all records stored in the proxy and removes all keys and values used to support the proxy from the
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");