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