Skip to content

Commit 6f4a68d

Browse files
committed
feat: initial code commit
1 parent f985129 commit 6f4a68d

1 file changed

Lines changed: 338 additions & 0 deletions

File tree

Lines changed: 338 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,338 @@
1+
(function() {
2+
'use strict';
3+
4+
// Promises!
5+
var Promise = (typeof module !== 'undefined' && module.exports) ?
6+
require('promise') : this.Promise;
7+
8+
var globalObject = this;
9+
var serializer = null;
10+
var sessionStorage = null;
11+
12+
function getSupport() {
13+
// If the app is running inside a Google Chrome packaged webapp, or some
14+
// other context where sessionStorage isn't available, we don't use
15+
// sessionStorage. This feature detection is preferred over the old
16+
// `if (window.chrome && window.chrome.runtime)` code.
17+
// See: https://github.com/mozilla/localForage/issues/68
18+
try {
19+
// If sessionStorage isn't available, we get outta here!
20+
// This should be inside a try catch
21+
if (globalObject.sessionStorage && ('setItem' in globalObject.sessionStorage)) {
22+
return true;
23+
}
24+
} catch (e) {
25+
26+
}
27+
28+
return false;
29+
}
30+
31+
if (getSupport()) {
32+
// Initialize sessionStorage and create a variable to use throughout
33+
// the code.
34+
sessionStorage = this.sessionStorage;
35+
} else {
36+
return;
37+
}
38+
39+
var ModuleType = {
40+
DEFINE: 1,
41+
EXPORT: 2,
42+
WINDOW: 3
43+
};
44+
45+
// Attaching to window (i.e. no module loader) is the assumed,
46+
// simple default.
47+
var moduleType = ModuleType.WINDOW;
48+
49+
// Find out what kind of module setup we have; if none, we'll just attach
50+
// localForage to the main window.
51+
if (typeof define === 'function' && define.amd) {
52+
moduleType = ModuleType.DEFINE;
53+
} else if (typeof module !== 'undefined' && module.exports) {
54+
moduleType = ModuleType.EXPORT;
55+
}
56+
57+
// Config the sessionStorage backend, using options set in the config.
58+
function _initStorage(options) {
59+
var self = this;
60+
var dbInfo = {};
61+
if (options) {
62+
for (var i in options) {
63+
dbInfo[i] = options[i];
64+
}
65+
}
66+
67+
dbInfo.keyPrefix = dbInfo.name + '/';
68+
69+
self._dbInfo = dbInfo;
70+
71+
var serializerPromise = new Promise(function(resolve/*, reject*/) {
72+
// We allow localForage to be declared as a module or as a
73+
// library available without AMD/require.js.
74+
if (moduleType === ModuleType.DEFINE) {
75+
require(['localforageSerializer'], resolve);
76+
} else if (moduleType === ModuleType.EXPORT) {
77+
// Making it browserify friendly
78+
resolve(require('./../utils/serializer'));
79+
} else {
80+
resolve(globalObject.localforageSerializer);
81+
}
82+
});
83+
84+
return serializerPromise.then(function(lib) {
85+
serializer = lib;
86+
return Promise.resolve();
87+
});
88+
}
89+
90+
// Remove all keys from the datastore, effectively destroying all data in
91+
// the app's key/value store!
92+
function clear(callback) {
93+
var self = this;
94+
var promise = self.ready().then(function() {
95+
var keyPrefix = self._dbInfo.keyPrefix;
96+
97+
for (var i = sessionStorage.length - 1; i >= 0; i--) {
98+
var key = sessionStorage.key(i);
99+
100+
if (key.indexOf(keyPrefix) === 0) {
101+
sessionStorage.removeItem(key);
102+
}
103+
}
104+
});
105+
106+
executeCallback(promise, callback);
107+
return promise;
108+
}
109+
110+
// Retrieve an item from the store. Unlike the original async_storage
111+
// library in Gaia, we don't modify return values at all. If a key's value
112+
// is `undefined`, we pass that value to the callback function.
113+
function getItem(key, callback) {
114+
var self = this;
115+
116+
// Cast the key to a string, as that's all we can set as a key.
117+
if (typeof key !== 'string') {
118+
window.console.warn(key +
119+
' used as a key, but it is not a string.');
120+
key = String(key);
121+
}
122+
123+
var promise = self.ready().then(function() {
124+
var dbInfo = self._dbInfo;
125+
var result = sessionStorage.getItem(dbInfo.keyPrefix + key);
126+
127+
// If a result was found, parse it from the serialized
128+
// string into a JS object. If result isn't truthy, the key
129+
// is likely undefined and we'll pass it straight to the
130+
// callback.
131+
if (result) {
132+
result = serializer.deserialize(result);
133+
}
134+
135+
return result;
136+
});
137+
138+
executeCallback(promise, callback);
139+
return promise;
140+
}
141+
142+
// Iterate over all items in the store.
143+
function iterate(iterator, callback) {
144+
var self = this;
145+
146+
var promise = self.ready().then(function() {
147+
var keyPrefix = self._dbInfo.keyPrefix;
148+
var keyPrefixLength = keyPrefix.length;
149+
var length = sessionStorage.length;
150+
151+
for (var i = 0; i < length; i++) {
152+
var key = sessionStorage.key(i);
153+
var value = sessionStorage.getItem(key);
154+
155+
// If a result was found, parse it from the serialized
156+
// string into a JS object. If result isn't truthy, the
157+
// key is likely undefined and we'll pass it straight
158+
// to the iterator.
159+
if (value) {
160+
value = serializer.deserialize(value);
161+
}
162+
163+
value = iterator(value, key.substring(keyPrefixLength), i + 1);
164+
165+
if (value !== void(0)) {
166+
return value;
167+
}
168+
}
169+
});
170+
171+
executeCallback(promise, callback);
172+
return promise;
173+
}
174+
175+
// Same as sessionStorage's key() method, except takes a callback.
176+
function key(n, callback) {
177+
var self = this;
178+
var promise = self.ready().then(function() {
179+
var dbInfo = self._dbInfo;
180+
var result;
181+
try {
182+
result = sessionStorage.key(n);
183+
} catch (error) {
184+
result = null;
185+
}
186+
187+
// Remove the prefix from the key, if a key is found.
188+
if (result) {
189+
result = result.substring(dbInfo.keyPrefix.length);
190+
}
191+
192+
return result;
193+
});
194+
195+
executeCallback(promise, callback);
196+
return promise;
197+
}
198+
199+
function keys(callback) {
200+
var self = this;
201+
var promise = self.ready().then(function() {
202+
var dbInfo = self._dbInfo;
203+
var length = sessionStorage.length;
204+
var keys = [];
205+
206+
for (var i = 0; i < length; i++) {
207+
if (sessionStorage.key(i).indexOf(dbInfo.keyPrefix) === 0) {
208+
keys.push(sessionStorage.key(i).substring(dbInfo.keyPrefix.length));
209+
}
210+
}
211+
212+
return keys;
213+
});
214+
215+
executeCallback(promise, callback);
216+
return promise;
217+
}
218+
219+
// Supply the number of keys in the datastore to the callback function.
220+
function length(callback) {
221+
var self = this;
222+
var promise = self.keys().then(function(keys) {
223+
return keys.length;
224+
});
225+
226+
executeCallback(promise, callback);
227+
return promise;
228+
}
229+
230+
// Remove an item from the store, nice and simple.
231+
function removeItem(key, callback) {
232+
var self = this;
233+
234+
// Cast the key to a string, as that's all we can set as a key.
235+
if (typeof key !== 'string') {
236+
window.console.warn(key +
237+
' used as a key, but it is not a string.');
238+
key = String(key);
239+
}
240+
241+
var promise = self.ready().then(function() {
242+
var dbInfo = self._dbInfo;
243+
sessionStorage.removeItem(dbInfo.keyPrefix + key);
244+
});
245+
246+
executeCallback(promise, callback);
247+
return promise;
248+
}
249+
250+
// Set a key's value and run an optional callback once the value is set.
251+
// Unlike Gaia's implementation, the callback function is passed the value,
252+
// in case you want to operate on that value only after you're sure it
253+
// saved, or something like that.
254+
function setItem(key, value, callback) {
255+
var self = this;
256+
257+
// Cast the key to a string, as that's all we can set as a key.
258+
if (typeof key !== 'string') {
259+
window.console.warn(key +
260+
' used as a key, but it is not a string.');
261+
key = String(key);
262+
}
263+
264+
var promise = self.ready().then(function() {
265+
// Convert undefined values to null.
266+
// https://github.com/mozilla/localForage/pull/42
267+
if (value === undefined) {
268+
value = null;
269+
}
270+
271+
// Save the original value to pass to the callback.
272+
var originalValue = value;
273+
274+
return new Promise(function(resolve, reject) {
275+
serializer.serialize(value, function(value, error) {
276+
if (error) {
277+
reject(error);
278+
} else {
279+
try {
280+
var dbInfo = self._dbInfo;
281+
sessionStorage.setItem(dbInfo.keyPrefix + key, value);
282+
resolve(originalValue);
283+
} catch (e) {
284+
// sessionStorage capacity exceeded.
285+
// TODO: Make this a specific error/event.
286+
if (e.name === 'QuotaExceededError' ||
287+
e.name === 'NS_ERROR_DOM_QUOTA_REACHED') {
288+
reject(e);
289+
}
290+
reject(e);
291+
}
292+
}
293+
});
294+
});
295+
});
296+
297+
executeCallback(promise, callback);
298+
return promise;
299+
}
300+
301+
function executeCallback(promise, callback) {
302+
if (callback) {
303+
promise.then(function(result) {
304+
callback(null, result);
305+
}, function(error) {
306+
callback(error);
307+
});
308+
}
309+
}
310+
311+
var sessionStorageWrapper = {
312+
_driver: 'sessionStorageWrapper',
313+
_initStorage: _initStorage,
314+
_support: function() {
315+
return new Promise(function(resolve/*, reject*/) {
316+
resolve(getSupport());
317+
});
318+
},
319+
iterate: iterate,
320+
getItem: getItem,
321+
setItem: setItem,
322+
removeItem: removeItem,
323+
clear: clear,
324+
length: length,
325+
key: key,
326+
keys: keys
327+
};
328+
329+
if (moduleType === ModuleType.DEFINE) {
330+
define('sessionStorageWrapper', function() {
331+
return sessionStorageWrapper;
332+
});
333+
} else if (moduleType === ModuleType.EXPORT) {
334+
module.exports = sessionStorageWrapper;
335+
} else {
336+
this.sessionStorageWrapper = sessionStorageWrapper;
337+
}
338+
}).call(window);

0 commit comments

Comments
 (0)