UNPKG

13.8 kBJavaScriptView Raw
1(function() {
2 'use strict';
3
4 var dummyStorage = {};
5
6 // Config the localStorage backend, using options set in the config.
7 function _initStorage(options) {
8 var self = this;
9
10 var dbInfo = {};
11 if (options) {
12 for (var i in options) {
13 dbInfo[i] = options[i];
14 }
15 }
16
17 dummyStorage[dbInfo.name] = dbInfo.db = {};
18
19 self._dbInfo = dbInfo;
20 return Promise.resolve();
21 }
22
23 var SERIALIZED_MARKER = '__lfsc__:';
24 var SERIALIZED_MARKER_LENGTH = SERIALIZED_MARKER.length;
25
26 // OMG the serializations!
27 var TYPE_ARRAYBUFFER = 'arbf';
28 var TYPE_BLOB = 'blob';
29 var TYPE_INT8ARRAY = 'si08';
30 var TYPE_UINT8ARRAY = 'ui08';
31 var TYPE_UINT8CLAMPEDARRAY = 'uic8';
32 var TYPE_INT16ARRAY = 'si16';
33 var TYPE_INT32ARRAY = 'si32';
34 var TYPE_UINT16ARRAY = 'ur16';
35 var TYPE_UINT32ARRAY = 'ui32';
36 var TYPE_FLOAT32ARRAY = 'fl32';
37 var TYPE_FLOAT64ARRAY = 'fl64';
38 var TYPE_SERIALIZED_MARKER_LENGTH = SERIALIZED_MARKER_LENGTH +
39 TYPE_ARRAYBUFFER.length;
40
41 function clear(callback) {
42 var self = this;
43 var promise = new Promise(function(resolve, reject) {
44 self.ready().then(function() {
45 var db = self._dbInfo.db;
46
47 for (var key in db) {
48 if (db.hasOwnProperty(key)) {
49 delete db[key];
50 // db[key] = undefined;
51 }
52 }
53
54 resolve();
55 }).catch(reject);
56 });
57
58 executeCallback(promise, callback);
59 return promise;
60 }
61
62 function getItem(key, callback) {
63 var self = this;
64
65 // Cast the key to a string, as that's all we can set as a key.
66 if (typeof key !== 'string') {
67 window.console.warn(key +
68 ' used as a key, but it is not a string.');
69 key = String(key);
70 }
71
72 var promise = new Promise(function(resolve, reject) {
73 self.ready().then(function() {
74 try {
75 var db = self._dbInfo.db;
76 var result = db[key];
77
78 if (result) {
79 result = _deserialize(result);
80 }
81
82 resolve(result);
83 } catch (e) {
84 reject(e);
85 }
86 }).catch(reject);
87 });
88
89 executeCallback(promise, callback);
90 return promise;
91 }
92
93 function iterate(callback) {
94 var self = this;
95
96 var promise = new Promise(function(resolve, reject) {
97 self.ready().then(function() {
98 try {
99 var db = self._dbInfo.db;
100
101 for (var key in db) {
102 var result = db[key];
103
104 if (result) {
105 result = _deserialize(result);
106 }
107
108 callback(result, key);
109 }
110
111 resolve();
112 } catch (e) {
113 reject(e);
114 }
115 }).catch(reject);
116 });
117
118 executeCallback(promise, callback);
119 return promise;
120 }
121
122 function key(n, callback) {
123 var self = this;
124 var promise = new Promise(function(resolve, reject) {
125 self.ready().then(function() {
126 var db = self._dbInfo.db;
127 var result = null;
128 var index = 0;
129
130 for (var key in db) {
131 if (db.hasOwnProperty(key) && db[key] !== undefined) {
132 if (n === index) {
133 result = key;
134 break;
135 }
136 index++;
137 }
138 }
139
140 resolve(result);
141 }).catch(reject);
142 });
143
144 executeCallback(promise, callback);
145 return promise;
146 }
147
148 function keys(callback) {
149 var self = this;
150 var promise = new Promise(function(resolve, reject) {
151 self.ready().then(function() {
152 var db = self._dbInfo.db;
153 var keys = [];
154
155 for (var key in db) {
156 if (db.hasOwnProperty(key)) {
157 keys.push(key);
158 }
159 }
160
161 resolve(keys);
162 }).catch(reject);
163 });
164
165 executeCallback(promise, callback);
166 return promise;
167 }
168
169 function length(callback) {
170 var self = this;
171 var promise = new Promise(function(resolve, reject) {
172 self.keys().then(function(keys) {
173 resolve(keys.length);
174 }).catch(reject);
175 });
176
177 executeCallback(promise, callback);
178 return promise;
179 }
180
181 function removeItem(key, callback) {
182 var self = this;
183
184 // Cast the key to a string, as that's all we can set as a key.
185 if (typeof key !== 'string') {
186 window.console.warn(key +
187 ' used as a key, but it is not a string.');
188 key = String(key);
189 }
190
191 var promise = new Promise(function(resolve, reject) {
192 self.ready().then(function() {
193 var db = self._dbInfo.db;
194 if (db.hasOwnProperty(key)) {
195 delete db[key];
196 // db[key] = undefined;
197 }
198
199 resolve();
200 }).catch(reject);
201 });
202
203 executeCallback(promise, callback);
204 return promise;
205 }
206
207 function setItem(key, value, callback) {
208 var self = this;
209
210 // Cast the key to a string, as that's all we can set as a key.
211 if (typeof key !== 'string') {
212 window.console.warn(key +
213 ' used as a key, but it is not a string.');
214 key = String(key);
215 }
216
217 var promise = new Promise(function(resolve, reject) {
218 self.ready().then(function() {
219 // Convert undefined values to null.
220 // https://github.com/mozilla/localForage/pull/42
221 if (value === undefined) {
222 value = null;
223 }
224
225 // Save the original value to pass to the callback.
226 var originalValue = value;
227
228 _serialize(value, function(value, error) {
229 if (error) {
230 reject(error);
231 } else {
232 try {
233 var db = self._dbInfo.db;
234 db[key] = value;
235 resolve(originalValue);
236 } catch (e) {
237 reject(e);
238 }
239 }
240 });
241 }).catch(reject);
242 });
243
244 executeCallback(promise, callback);
245 return promise;
246 }
247
248 // _serialize just like in LocalStorage
249 function _serialize(value, callback) {
250 var valueString = '';
251 if (value) {
252 valueString = value.toString();
253 }
254
255 // Cannot use `value instanceof ArrayBuffer` or such here, as these
256 // checks fail when running the tests using casper.js...
257 //
258 // TODO: See why those tests fail and use a better solution.
259 if (value && (value.toString() === '[object ArrayBuffer]' ||
260 value.buffer &&
261 value.buffer.toString() === '[object ArrayBuffer]')) {
262 // Convert binary arrays to a string and prefix the string with
263 // a special marker.
264 var buffer;
265 var marker = SERIALIZED_MARKER;
266
267 if (value instanceof ArrayBuffer) {
268 buffer = value;
269 marker += TYPE_ARRAYBUFFER;
270 } else {
271 buffer = value.buffer;
272
273 if (valueString === '[object Int8Array]') {
274 marker += TYPE_INT8ARRAY;
275 } else if (valueString === '[object Uint8Array]') {
276 marker += TYPE_UINT8ARRAY;
277 } else if (valueString === '[object Uint8ClampedArray]') {
278 marker += TYPE_UINT8CLAMPEDARRAY;
279 } else if (valueString === '[object Int16Array]') {
280 marker += TYPE_INT16ARRAY;
281 } else if (valueString === '[object Uint16Array]') {
282 marker += TYPE_UINT16ARRAY;
283 } else if (valueString === '[object Int32Array]') {
284 marker += TYPE_INT32ARRAY;
285 } else if (valueString === '[object Uint32Array]') {
286 marker += TYPE_UINT32ARRAY;
287 } else if (valueString === '[object Float32Array]') {
288 marker += TYPE_FLOAT32ARRAY;
289 } else if (valueString === '[object Float64Array]') {
290 marker += TYPE_FLOAT64ARRAY;
291 } else {
292 callback(new Error('Failed to get type for BinaryArray'));
293 }
294 }
295
296 callback(marker + _bufferToString(buffer));
297 } else if (valueString === '[object Blob]') {
298 // Conver the blob to a binaryArray and then to a string.
299 var fileReader = new FileReader();
300
301 fileReader.onload = function() {
302 var str = _bufferToString(this.result);
303
304 callback(SERIALIZED_MARKER + TYPE_BLOB + str);
305 };
306
307 fileReader.readAsArrayBuffer(value);
308 } else {
309 try {
310 callback(JSON.stringify(value));
311 } catch (e) {
312 window.console.error("Couldn't convert value into a JSON " +
313 'string: ', value);
314
315 callback(e);
316 }
317 }
318 }
319
320 // _deserialize just like in LocalStorage
321 function _deserialize(value) {
322 // If we haven't marked this string as being specially serialized (i.e.
323 // something other than serialized JSON), we can just return it and be
324 // done with it.
325 if (value.substring(0,
326 SERIALIZED_MARKER_LENGTH) !== SERIALIZED_MARKER) {
327 return JSON.parse(value);
328 }
329
330 // The following code deals with deserializing some kind of Blob or
331 // TypedArray. First we separate out the type of data we're dealing
332 // with from the data itself.
333 var serializedString = value.substring(TYPE_SERIALIZED_MARKER_LENGTH);
334 var type = value.substring(SERIALIZED_MARKER_LENGTH,
335 TYPE_SERIALIZED_MARKER_LENGTH);
336
337 // Fill the string into a ArrayBuffer.
338 // 2 bytes for each char.
339 var buffer = new ArrayBuffer(serializedString.length * 2);
340 var bufferView = new Uint16Array(buffer);
341 for (var i = serializedString.length - 1; i >= 0; i--) {
342 bufferView[i] = serializedString.charCodeAt(i);
343 }
344
345 // Return the right type based on the code/type set during
346 // serialization.
347 switch (type) {
348 case TYPE_ARRAYBUFFER:
349 return buffer;
350 case TYPE_BLOB:
351 return new Blob([buffer]);
352 case TYPE_INT8ARRAY:
353 return new Int8Array(buffer);
354 case TYPE_UINT8ARRAY:
355 return new Uint8Array(buffer);
356 case TYPE_UINT8CLAMPEDARRAY:
357 return new Uint8ClampedArray(buffer);
358 case TYPE_INT16ARRAY:
359 return new Int16Array(buffer);
360 case TYPE_UINT16ARRAY:
361 return new Uint16Array(buffer);
362 case TYPE_INT32ARRAY:
363 return new Int32Array(buffer);
364 case TYPE_UINT32ARRAY:
365 return new Uint32Array(buffer);
366 case TYPE_FLOAT32ARRAY:
367 return new Float32Array(buffer);
368 case TYPE_FLOAT64ARRAY:
369 return new Float64Array(buffer);
370 default:
371 throw new Error('Unkown type: ' + type);
372 }
373 }
374
375 // _bufferToString just like in LocalStorage
376 function _bufferToString(buffer) {
377 var str = '';
378 var uint16Array = new Uint16Array(buffer);
379
380 try {
381 str = String.fromCharCode.apply(null, uint16Array);
382 } catch (e) {
383 // This is a fallback implementation in case the first one does
384 // not work. This is required to get the phantomjs passing...
385 for (var i = 0; i < uint16Array.length; i++) {
386 str += String.fromCharCode(uint16Array[i]);
387 }
388 }
389
390 return str;
391 }
392
393 function executeCallback(promise, callback) {
394 if (callback) {
395 promise.then(function(result) {
396 callback(null, result);
397 }, function(error) {
398 callback(error);
399 });
400 }
401 }
402
403 var dummyStorageDriver = {
404 _driver: 'dummyStorageDriver',
405 _initStorage: _initStorage,
406 // _supports: function() { return true; }
407 iterate: iterate,
408 getItem: getItem,
409 setItem: setItem,
410 removeItem: removeItem,
411 clear: clear,
412 length: length,
413 key: key,
414 keys: keys
415 };
416
417 if (typeof define === 'function' && define.amd) {
418 define('dummyStorageDriver', function() {
419 return dummyStorageDriver;
420 });
421 } else if (typeof module !== 'undefined' && module.exports && typeof require !== 'undefined') {
422 module.exports = dummyStorageDriver;
423 } else {
424 this.dummyStorageDriver = dummyStorageDriver;
425 }
426}).call(window);