|
1 YUI.add('cache-offline', function (Y, NAME) { |
|
2 |
|
3 /** |
|
4 * Provides a Cache subclass which uses HTML5 `localStorage` for persistence. |
|
5 * |
|
6 * @module cache |
|
7 * @submodule cache-offline |
|
8 */ |
|
9 |
|
10 /** |
|
11 * Extends Cache utility with offline functionality. |
|
12 * @class CacheOffline |
|
13 * @extends Cache |
|
14 * @constructor |
|
15 */ |
|
16 function CacheOffline() { |
|
17 CacheOffline.superclass.constructor.apply(this, arguments); |
|
18 } |
|
19 |
|
20 var localStorage = null, |
|
21 JSON = Y.JSON; |
|
22 |
|
23 // Bug 2529572 |
|
24 try { |
|
25 localStorage = Y.config.win.localStorage; |
|
26 } |
|
27 catch(e) { |
|
28 } |
|
29 |
|
30 ///////////////////////////////////////////////////////////////////////////// |
|
31 // |
|
32 // CacheOffline events |
|
33 // |
|
34 ///////////////////////////////////////////////////////////////////////////// |
|
35 |
|
36 /** |
|
37 * @event error |
|
38 * @description Fired when an entry could not be added, most likely due to |
|
39 * exceeded browser quota. |
|
40 * <dl> |
|
41 * <dt>error (Object)</dt> <dd>The error object.</dd> |
|
42 * </dl> |
|
43 */ |
|
44 |
|
45 ///////////////////////////////////////////////////////////////////////////// |
|
46 // |
|
47 // CacheOffline static |
|
48 // |
|
49 ///////////////////////////////////////////////////////////////////////////// |
|
50 Y.mix(CacheOffline, { |
|
51 /** |
|
52 * Class name. |
|
53 * |
|
54 * @property NAME |
|
55 * @type String |
|
56 * @static |
|
57 * @final |
|
58 * @value "cacheOffline" |
|
59 */ |
|
60 NAME: "cacheOffline", |
|
61 |
|
62 ATTRS: { |
|
63 ///////////////////////////////////////////////////////////////////////////// |
|
64 // |
|
65 // CacheOffline Attributes |
|
66 // |
|
67 ///////////////////////////////////////////////////////////////////////////// |
|
68 |
|
69 /** |
|
70 * @attribute sandbox |
|
71 * @description A string that must be passed in via the constructor. |
|
72 * This identifier is used to sandbox one cache instance's entries |
|
73 * from another. Calling the cache instance's flush and length methods |
|
74 * or get("entries") will apply to only these sandboxed entries. |
|
75 * @type String |
|
76 * @default "default" |
|
77 * @initOnly |
|
78 */ |
|
79 sandbox: { |
|
80 value: "default", |
|
81 writeOnce: "initOnly" |
|
82 }, |
|
83 |
|
84 /** |
|
85 * @attribute expires |
|
86 * @description Absolute Date when data expires or |
|
87 * relative number of milliseconds. Zero disables expiration. |
|
88 * @type Date | Number |
|
89 * @default 86400000 (one day) |
|
90 */ |
|
91 expires: { |
|
92 value: 86400000 |
|
93 }, |
|
94 |
|
95 /** |
|
96 * @attribute max |
|
97 * @description Disabled. |
|
98 * @readOnly |
|
99 * @default null |
|
100 */ |
|
101 max: { |
|
102 value: null, |
|
103 readOnly: true |
|
104 }, |
|
105 |
|
106 /** |
|
107 * @attribute uniqueKeys |
|
108 * @description Always true for CacheOffline. |
|
109 * @readOnly |
|
110 * @default true |
|
111 */ |
|
112 uniqueKeys: { |
|
113 value: true, |
|
114 readOnly: true, |
|
115 setter: function() { |
|
116 return true; |
|
117 } |
|
118 } |
|
119 }, |
|
120 |
|
121 /** |
|
122 * Removes all items from all sandboxes. Useful if localStorage has |
|
123 * exceeded quota. Only supported on browsers that implement HTML 5 |
|
124 * localStorage. |
|
125 * |
|
126 * @method flushAll |
|
127 * @static |
|
128 */ |
|
129 flushAll: function() { |
|
130 var store = localStorage, key; |
|
131 if(store) { |
|
132 if(store.clear) { |
|
133 store.clear(); |
|
134 } |
|
135 // FF2.x and FF3.0.x |
|
136 else { |
|
137 for (key in store) { |
|
138 if (store.hasOwnProperty(key)) { |
|
139 store.removeItem(key); |
|
140 delete store[key]; |
|
141 } |
|
142 } |
|
143 } |
|
144 } |
|
145 else { |
|
146 } |
|
147 } |
|
148 }); |
|
149 |
|
150 Y.extend(CacheOffline, Y.Cache, localStorage ? { |
|
151 ///////////////////////////////////////////////////////////////////////////// |
|
152 // |
|
153 // Offline is supported |
|
154 // |
|
155 ///////////////////////////////////////////////////////////////////////////// |
|
156 |
|
157 ///////////////////////////////////////////////////////////////////////////// |
|
158 // |
|
159 // CacheOffline protected methods |
|
160 // |
|
161 ///////////////////////////////////////////////////////////////////////////// |
|
162 /** |
|
163 * Always return null. |
|
164 * |
|
165 * @method _setMax |
|
166 * @protected |
|
167 */ |
|
168 _setMax: function(value) { |
|
169 return null; |
|
170 }, |
|
171 |
|
172 /** |
|
173 * Gets size. |
|
174 * |
|
175 * @method _getSize |
|
176 * @protected |
|
177 */ |
|
178 _getSize: function() { |
|
179 var count = 0, |
|
180 i=0, |
|
181 l=localStorage.length; |
|
182 for(; i<l; ++i) { |
|
183 // Match sandbox id |
|
184 if(localStorage.key(i).indexOf(this.get("sandbox")) === 0) { |
|
185 count++; |
|
186 } |
|
187 } |
|
188 return count; |
|
189 }, |
|
190 |
|
191 /** |
|
192 * Gets all entries. |
|
193 * |
|
194 * @method _getEntries |
|
195 * @protected |
|
196 */ |
|
197 _getEntries: function() { |
|
198 var entries = [], |
|
199 i=0, |
|
200 l=localStorage.length, |
|
201 sandbox = this.get("sandbox"); |
|
202 for(; i<l; ++i) { |
|
203 // Match sandbox id |
|
204 if(localStorage.key(i).indexOf(sandbox) === 0) { |
|
205 entries[i] = JSON.parse(localStorage.key(i).substring(sandbox.length)); |
|
206 } |
|
207 } |
|
208 return entries; |
|
209 }, |
|
210 |
|
211 /** |
|
212 * Adds entry to cache. |
|
213 * |
|
214 * @method _defAddFn |
|
215 * @param e {EventFacade} Event Facade with the following properties: |
|
216 * <dl> |
|
217 * <dt>entry (Object)</dt> <dd>The cached entry.</dd> |
|
218 * </dl> |
|
219 * @protected |
|
220 */ |
|
221 _defAddFn: function(e) { |
|
222 var entry = e.entry, |
|
223 request = entry.request, |
|
224 cached = entry.cached, |
|
225 expires = entry.expires; |
|
226 |
|
227 // Convert Dates to msecs on the way into localStorage |
|
228 entry.cached = cached.getTime(); |
|
229 entry.expires = expires ? expires.getTime() : expires; |
|
230 |
|
231 try { |
|
232 localStorage.setItem(this.get("sandbox")+JSON.stringify({"request":request}), JSON.stringify(entry)); |
|
233 } |
|
234 catch(error) { |
|
235 this.fire("error", {error:error}); |
|
236 } |
|
237 }, |
|
238 |
|
239 /** |
|
240 * Flushes cache. |
|
241 * |
|
242 * @method _defFlushFn |
|
243 * @param e {EventFacade} Event Facade object. |
|
244 * @protected |
|
245 */ |
|
246 _defFlushFn: function(e) { |
|
247 var key, |
|
248 i=localStorage.length-1; |
|
249 for(; i>-1; --i) { |
|
250 // Match sandbox id |
|
251 key = localStorage.key(i); |
|
252 if(key.indexOf(this.get("sandbox")) === 0) { |
|
253 localStorage.removeItem(key); |
|
254 } |
|
255 } |
|
256 }, |
|
257 |
|
258 ///////////////////////////////////////////////////////////////////////////// |
|
259 // |
|
260 // CacheOffline public methods |
|
261 // |
|
262 ///////////////////////////////////////////////////////////////////////////// |
|
263 /** |
|
264 * Adds a new entry to the cache of the format |
|
265 * {request:request, response:response, cached:cached, expires: expires}. |
|
266 * |
|
267 * @method add |
|
268 * @param request {Object} Request value must be a String or JSON. |
|
269 * @param response {Object} Response value must be a String or JSON. |
|
270 */ |
|
271 |
|
272 /** |
|
273 * Retrieves cached object for given request, if available. |
|
274 * Returns null if there is no cache match. |
|
275 * |
|
276 * @method retrieve |
|
277 * @param request {Object} Request object. |
|
278 * @return {Object} Cached object with the properties request, response, |
|
279 * and expires, or null. |
|
280 */ |
|
281 retrieve: function(request) { |
|
282 this.fire("request", {request: request}); |
|
283 |
|
284 var entry, expires, sandboxedrequest; |
|
285 |
|
286 try { |
|
287 sandboxedrequest = this.get("sandbox")+JSON.stringify({"request":request}); |
|
288 try { |
|
289 entry = JSON.parse(localStorage.getItem(sandboxedrequest)); |
|
290 } |
|
291 catch(e) { |
|
292 } |
|
293 } |
|
294 catch(e2) { |
|
295 } |
|
296 |
|
297 if(entry) { |
|
298 // Convert msecs to Dates on the way out of localStorage |
|
299 entry.cached = new Date(entry.cached); |
|
300 expires = entry.expires; |
|
301 expires = !expires ? null : new Date(expires); |
|
302 entry.expires = expires; |
|
303 |
|
304 if(this._isMatch(request, entry)) { |
|
305 this.fire("retrieve", {entry: entry}); |
|
306 return entry; |
|
307 } |
|
308 } |
|
309 return null; |
|
310 } |
|
311 } : |
|
312 ///////////////////////////////////////////////////////////////////////////// |
|
313 // |
|
314 // Offline is not supported |
|
315 // |
|
316 ///////////////////////////////////////////////////////////////////////////// |
|
317 { |
|
318 /** |
|
319 * Always return null. |
|
320 * |
|
321 * @method _setMax |
|
322 * @protected |
|
323 */ |
|
324 _setMax: function(value) { |
|
325 return null; |
|
326 } |
|
327 }); |
|
328 |
|
329 |
|
330 Y.CacheOffline = CacheOffline; |
|
331 |
|
332 |
|
333 }, '@VERSION@', {"requires": ["cache-base", "json"]}); |