Upgrade to ExtJS 4.0.7 - Released 10/19/2011
[extjs.git] / docs / source / WebStorage.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="../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; }
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-proxy-WebStorage'>/**
19 </span> * @author Ed Spencer
20  *
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.
24  * @private
25  */
26 Ext.define('Ext.data.proxy.WebStorage', {
27     extend: 'Ext.data.proxy.Client',
28     alternateClassName: 'Ext.data.WebStorageProxy',
29
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.
33      */
34     id: undefined,
35
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.
39      */
40     constructor: function(config) {
41         this.callParent(arguments);
42
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.
46          */
47         this.cache = {};
48
49         //&lt;debug&gt;
50         if (this.getStorageObject() === undefined) {
51             Ext.Error.raise(&quot;Local Storage is not supported in this browser, please use another type of data proxy&quot;);
52         }
53         //&lt;/debug&gt;
54
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);
57
58         //&lt;debug&gt;
59         if (this.id === undefined) {
60             Ext.Error.raise(&quot;No unique id was provided to the local storage proxy. See Ext.data.proxy.LocalStorage documentation for details&quot;);
61         }
62         //&lt;/debug&gt;
63
64         this.initialize();
65     },
66
67     //inherit docs
68     create: function(operation, callback, scope) {
69         var records = operation.records,
70             length  = records.length,
71             ids     = this.getIds(),
72             id, record, i;
73
74         operation.setStarted();
75
76         for (i = 0; i &lt; length; i++) {
77             record = records[i];
78
79             if (record.phantom) {
80                 record.phantom = false;
81                 id = this.getNextId();
82             } else {
83                 id = record.getId();
84             }
85
86             this.setRecord(record, id);
87             ids.push(id);
88         }
89
90         this.setIds(ids);
91
92         operation.setCompleted();
93         operation.setSuccessful();
94
95         if (typeof callback == 'function') {
96             callback.call(scope || this, operation);
97         }
98     },
99
100     //inherit docs
101     read: function(operation, callback, scope) {
102         //TODO: respect sorters, filters, start and limit options on the Operation
103
104         var records = [],
105             ids     = this.getIds(),
106             length  = ids.length,
107             i, recordData, record;
108
109         //read a single record
110         if (operation.id) {
111             record = this.getRecord(operation.id);
112
113             if (record) {
114                 records.push(record);
115                 operation.setSuccessful();
116             }
117         } else {
118             for (i = 0; i &lt; length; i++) {
119                 records.push(this.getRecord(ids[i]));
120             }
121             operation.setSuccessful();
122         }
123
124         operation.setCompleted();
125
126         operation.resultSet = Ext.create('Ext.data.ResultSet', {
127             records: records,
128             total  : records.length,
129             loaded : true
130         });
131
132         if (typeof callback == 'function') {
133             callback.call(scope || this, operation);
134         }
135     },
136
137     //inherit docs
138     update: function(operation, callback, scope) {
139         var records = operation.records,
140             length  = records.length,
141             ids     = this.getIds(),
142             record, id, i;
143
144         operation.setStarted();
145
146         for (i = 0; i &lt; length; i++) {
147             record = records[i];
148             this.setRecord(record);
149
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
152             id = record.getId();
153             if (id !== undefined &amp;&amp; Ext.Array.indexOf(ids, id) == -1) {
154                 ids.push(id);
155             }
156         }
157         this.setIds(ids);
158
159         operation.setCompleted();
160         operation.setSuccessful();
161
162         if (typeof callback == 'function') {
163             callback.call(scope || this, operation);
164         }
165     },
166
167     //inherit
168     destroy: function(operation, callback, scope) {
169         var records = operation.records,
170             length  = records.length,
171             ids     = this.getIds(),
172
173             //newIds is a copy of ids, from which we remove the destroyed records
174             newIds  = [].concat(ids),
175             i;
176
177         for (i = 0; i &lt; length; i++) {
178             Ext.Array.remove(newIds, records[i].getId());
179             this.removeRecord(records[i], false);
180         }
181
182         this.setIds(newIds);
183
184         operation.setCompleted();
185         operation.setSuccessful();
186
187         if (typeof callback == 'function') {
188             callback.call(scope || this, operation);
189         }
190     },
191
192 <span id='Ext-data-proxy-WebStorage-method-getRecord'>    /**
193 </span>     * @private
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
197      */
198     getRecord: function(id) {
199         if (this.cache[id] === undefined) {
200             var rawData = Ext.decode(this.getStorageObject().getItem(this.getRecordKey(id))),
201                 data    = {},
202                 Model   = this.model,
203                 fields  = Model.prototype.fields.items,
204                 length  = fields.length,
205                 i, field, name, record;
206
207             for (i = 0; i &lt; length; i++) {
208                 field = fields[i];
209                 name  = field.name;
210
211                 if (typeof field.decode == 'function') {
212                     data[name] = field.decode(rawData[name]);
213                 } else {
214                     data[name] = rawData[name];
215                 }
216             }
217
218             record = new Model(data, id);
219             record.phantom = false;
220
221             this.cache[id] = record;
222         }
223
224         return this.cache[id];
225     },
226
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)
231      */
232     setRecord: function(record, id) {
233         if (id) {
234             record.setId(id);
235         } else {
236             id = record.getId();
237         }
238
239         var me = this,
240             rawData = record.data,
241             data    = {},
242             model   = me.model,
243             fields  = model.prototype.fields.items,
244             length  = fields.length,
245             i = 0,
246             field, name, obj, key;
247
248         for (; i &lt; length; i++) {
249             field = fields[i];
250             name  = field.name;
251
252             if (typeof field.encode == 'function') {
253                 data[name] = field.encode(rawData[name], record);
254             } else {
255                 data[name] = rawData[name];
256             }
257         }
258
259         obj = me.getStorageObject();
260         key = me.getRecordKey(id);
261
262         //keep the cache up to date
263         me.cache[id] = record;
264
265         //iPad bug requires that we remove the item before setting it
266         obj.removeItem(key);
267         obj.setItem(key, Ext.encode(data));
268     },
269
270 <span id='Ext-data-proxy-WebStorage-method-removeRecord'>    /**
271 </span>     * @private
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
275      */
276     removeRecord: function(id, updateIds) {
277         var me = this,
278             ids;
279
280         if (id.isModel) {
281             id = id.getId();
282         }
283
284         if (updateIds !== false) {
285             ids = me.getIds();
286             Ext.Array.remove(ids, id);
287             me.setIds(ids);
288         }
289
290         me.getStorageObject().removeItem(me.getRecordKey(id));
291     },
292
293 <span id='Ext-data-proxy-WebStorage-method-getRecordKey'>    /**
294 </span>     * @private
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
299      */
300     getRecordKey: function(id) {
301         if (id.isModel) {
302             id = id.getId();
303         }
304
305         return Ext.String.format(&quot;{0}-{1}&quot;, this.id, id);
306     },
307
308 <span id='Ext-data-proxy-WebStorage-method-getRecordCounterKey'>    /**
309 </span>     * @private
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
313      */
314     getRecordCounterKey: function() {
315         return Ext.String.format(&quot;{0}-counter&quot;, this.id);
316     },
317
318 <span id='Ext-data-proxy-WebStorage-method-getIds'>    /**
319 </span>     * @private
320      * Returns the array of record IDs stored in this Proxy
321      * @return {Number[]} The record IDs. Each is cast as a Number
322      */
323     getIds: function() {
324         var ids    = (this.getStorageObject().getItem(this.id) || &quot;&quot;).split(&quot;,&quot;),
325             length = ids.length,
326             i;
327
328         if (length == 1 &amp;&amp; ids[0] === &quot;&quot;) {
329             ids = [];
330         } else {
331             for (i = 0; i &lt; length; i++) {
332                 ids[i] = parseInt(ids[i], 10);
333             }
334         }
335
336         return ids;
337     },
338
339 <span id='Ext-data-proxy-WebStorage-method-setIds'>    /**
340 </span>     * @private
341      * Saves the array of ids representing the set of all records in the Proxy
342      * @param {Number[]} ids The ids to set
343      */
344     setIds: function(ids) {
345         var obj = this.getStorageObject(),
346             str = ids.join(&quot;,&quot;);
347
348         obj.removeItem(this.id);
349
350         if (!Ext.isEmpty(str)) {
351             obj.setItem(this.id, str);
352         }
353     },
354
355 <span id='Ext-data-proxy-WebStorage-method-getNextId'>    /**
356 </span>     * @private
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
360      */
361     getNextId: function() {
362         var obj  = this.getStorageObject(),
363             key  = this.getRecordCounterKey(),
364             last = obj.getItem(key),
365             ids, id;
366
367         if (last === null) {
368             ids = this.getIds();
369             last = ids[ids.length - 1] || 0;
370         }
371
372         id = parseInt(last, 10) + 1;
373         obj.setItem(key, id);
374
375         return id;
376     },
377
378 <span id='Ext-data-proxy-WebStorage-method-initialize'>    /**
379 </span>     * @private
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.
382      */
383     initialize: function() {
384         var storageObject = this.getStorageObject();
385         storageObject.setItem(this.id, storageObject.getItem(this.id) || &quot;&quot;);
386     },
387
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
390      * storage object.
391      */
392     clear: function() {
393         var obj = this.getStorageObject(),
394             ids = this.getIds(),
395             len = ids.length,
396             i;
397
398         //remove all the records
399         for (i = 0; i &lt; len; i++) {
400             this.removeRecord(ids[i]);
401         }
402
403         //remove the supporting objects
404         obj.removeItem(this.getRecordCounterKey());
405         obj.removeItem(this.id);
406     },
407
408 <span id='Ext-data-proxy-WebStorage-method-getStorageObject'>    /**
409 </span>     * @private
410      * Abstract function which should return the storage object that data will be saved to. This must be implemented
411      * in each subclass.
412      * @return {Object} The storage object
413      */
414     getStorageObject: function() {
415         //&lt;debug&gt;
416         Ext.Error.raise(&quot;The getStorageObject function has not been defined in your Ext.data.proxy.WebStorage subclass&quot;);
417         //&lt;/debug&gt;
418     }
419 });</pre>
420 </body>
421 </html>