localstorage-down
Advanced tools
Comparing version 0.5.2 to 0.6.0
265
index.js
'use strict'; | ||
var util = require('util'); | ||
var inherits = require('inherits'); | ||
var AbstractLevelDOWN = require('abstract-leveldown').AbstractLevelDOWN; | ||
var AbstractIterator = require('abstract-leveldown').AbstractIterator; | ||
var noop = function () { | ||
}; | ||
var LocalStorage = require('./localstorage').LocalStorage; | ||
var LocalStorageCore = require('./localstorage-core'); | ||
// see http://stackoverflow.com/a/15349865/680742 | ||
var nextTick = global.setImmediate || process.nextTick; | ||
@@ -14,6 +17,5 @@ | ||
this._dbsize = this.db.container.length(); | ||
this._reverse = !!options.reverse; | ||
this._end = options.end; | ||
this._start = options.start; | ||
this._endkey = options.end; | ||
this._startkey = options.start; | ||
this._gt = options.gt; | ||
@@ -27,44 +29,99 @@ this._gte = options.gte; | ||
if (this._start) { | ||
this._pos = this.db.container.indexOfKey(this._start); | ||
if (this._reverse) { | ||
if (this._exclusiveStart || this.db.container.key(this._pos) !== this._start) { | ||
this._pos--; | ||
} | ||
} else if (this._exclusiveStart && this.db.container.key(this._pos) === this._start) { | ||
this._pos++; | ||
} | ||
} else { | ||
this._pos = this._reverse ? this._dbsize - 1 : 0; | ||
} | ||
this.onInitCompleteListeners = []; | ||
} | ||
util.inherits(LDIterator, AbstractIterator); | ||
inherits(LDIterator, AbstractIterator); | ||
LDIterator.prototype._init = function (callback) { | ||
var self = this; | ||
self.db.container.length(function (err, len) { | ||
if (err) { | ||
return callback(err); | ||
} | ||
if (self._startkey) { | ||
self.db.container.binarySearch(self._startkey, function (err, res) { | ||
if (err) { | ||
return callback(err); | ||
} | ||
self._pos = res.index; | ||
var startkey = res.key; | ||
if (self._reverse) { | ||
if (self._exclusiveStart || startkey !== self._startkey) { | ||
self._pos--; | ||
} | ||
} else if (self._exclusiveStart && startkey === self._startkey) { | ||
self._pos++; | ||
} | ||
callback(); | ||
}); | ||
} else { | ||
self._pos = self._reverse ? len - 1 : 0; | ||
callback(); | ||
} | ||
}); | ||
}; | ||
LDIterator.prototype._next = function (callback) { | ||
var self = this; | ||
if (this._pos >= this.db.container.length() || this._pos < 0) { | ||
return nextTick(callback); | ||
} | ||
var key = this.db.container.key(this._pos); | ||
function onInitComplete() { | ||
self.db.container.getKeyAt(self._pos, function (err, key) { | ||
if (err) { | ||
return callback(err); | ||
} | ||
if (!!this._end && (this._reverse ? key < this._end : key > this._end)) { | ||
return nextTick(callback); | ||
} | ||
if (typeof key === 'undefined') { // done reading | ||
return callback(); | ||
} | ||
if (!!this._limit && this._limit > 0 && this._count++ >= this._limit) { | ||
return nextTick(callback); | ||
if (!!self._endkey && (self._reverse ? key < self._endkey : key > self._endkey)) { | ||
return callback(); | ||
} | ||
if (!!self._limit && self._limit > 0 && self._count++ >= self._limit) { | ||
return callback(); | ||
} | ||
if ((self._lt && key >= self._lt) || | ||
(self._lte && key > self._lte) || | ||
(self._gt && key <= self._gt) || | ||
(self._gte && key < self._gte)) { | ||
return callback(); | ||
} | ||
self._pos += self._reverse ? -1 : 1; | ||
self.db.container.getItem(key, function (err, value) { | ||
if (err) { | ||
if (err.message === 'NotFound') { | ||
return callback(); | ||
} | ||
return callback(err); | ||
} | ||
callback(null, key, value); | ||
}); | ||
}); | ||
} | ||
if (!self.initStarted) { | ||
self.initStarted = true; | ||
self._init(function (err) { | ||
if (err) { | ||
return callback(err); | ||
} | ||
onInitComplete(); | ||
if ((this._lt && key >= this._lt) || | ||
(this._lte && key > this._lte) || | ||
(this._gt && key <= this._gt) || | ||
(this._gte && key < this._gte)) { | ||
return nextTick(callback); | ||
self.initCompleted = true; | ||
var i = -1; | ||
while (++i < self.onInitCompleteListeners) { | ||
nextTick(self.onInitCompleteListeners[i]); | ||
} | ||
}); | ||
} else if (!self.initCompleted) { | ||
self.onInitCompleteListeners.push(function () { | ||
onInitComplete(); | ||
}); | ||
} else { | ||
onInitComplete(); | ||
} | ||
var value = this.db.container.getItem(key); | ||
this._pos += this._reverse ? -1 : 1; | ||
nextTick(function () { callback(null, key, value); }); | ||
}; | ||
@@ -77,12 +134,9 @@ | ||
AbstractLevelDOWN.call(this, location); | ||
var Wstore = require('./localstorage').LocalStorage; | ||
this.container = new Wstore(location); | ||
this.container = new LocalStorage(location); | ||
} | ||
util.inherits(LD, AbstractLevelDOWN); | ||
inherits(LD, AbstractLevelDOWN); | ||
LD.prototype._open = function (options, callback) { | ||
nextTick(function () { | ||
callback(null, this); | ||
}.bind(this)); | ||
this.container.init(callback); | ||
}; | ||
@@ -95,3 +149,5 @@ | ||
if (err) { | ||
return callback(err); | ||
return nextTick(function () { | ||
callback(err); | ||
}); | ||
} | ||
@@ -102,3 +158,5 @@ | ||
if (err) { | ||
return callback(err); | ||
return nextTick(function () { | ||
callback(err); | ||
}); | ||
} | ||
@@ -113,4 +171,3 @@ | ||
this.container.setItem(key, value); | ||
nextTick(callback); | ||
this.container.setItem(key, value, callback); | ||
}; | ||
@@ -123,3 +180,5 @@ | ||
if (err) { | ||
return callback(err); | ||
return nextTick(function () { | ||
callback(err); | ||
}); | ||
} | ||
@@ -130,25 +189,19 @@ | ||
} | ||
var value = this.container.getItem(key); | ||
this.container.getItem(key, function (err, value) { | ||
if (value === undefined) { | ||
// 'NotFound' error, consistent with LevelDOWN API | ||
return nextTick(function () { | ||
callback(new Error('NotFound: ')); | ||
}); | ||
} | ||
if (err) { | ||
return callback(err); | ||
} | ||
if (options.asBuffer !== false && !Buffer.isBuffer(value)) { | ||
value = new Buffer(value); | ||
} | ||
if (options.asBuffer !== false && !Buffer.isBuffer(value)) { | ||
value = new Buffer(value); | ||
} | ||
if (options.asBuffer === false) { | ||
if (value.indexOf("{\"storetype\":\"json\",\"data\"") > -1) { | ||
var res = JSON.parse(value); | ||
value = res.data; | ||
if (options.asBuffer === false) { | ||
if (value.indexOf("{\"storetype\":\"json\",\"data\"") > -1) { | ||
var res = JSON.parse(value); | ||
value = res.data; | ||
} | ||
} | ||
} | ||
nextTick(function () { | ||
callback(null, value); | ||
@@ -163,3 +216,5 @@ }); | ||
if (err) { | ||
return callback(err); | ||
return nextTick(function () { | ||
callback(err); | ||
}); | ||
} | ||
@@ -170,33 +225,49 @@ if (!isBuffer(key)) { | ||
this.container.removeItem(key); | ||
nextTick(callback); | ||
this.container.removeItem(key, callback); | ||
}; | ||
LD.prototype._batch = function (array, options, callback) { | ||
var err; | ||
var i = 0; | ||
var key; | ||
var value; | ||
if (Array.isArray(array)) { | ||
for (; i < array.length; i++) { | ||
if (array[i]) { | ||
key = Buffer.isBuffer(array[i].key) ? array[i].key : String(array[i].key); | ||
err = checkKeyValue(key, 'key'); | ||
if (err) { | ||
return nextTick(callback.bind(null, err)); | ||
} | ||
if (array[i].type === 'del') { | ||
this._del(array[i].key, options, noop); | ||
} else if (array[i].type === 'put') { | ||
value = Buffer.isBuffer(array[i].value) ? array[i].value : String(array[i].value); | ||
err = checkKeyValue(value, 'value'); | ||
var self = this; | ||
nextTick(function () { | ||
var err; | ||
var key; | ||
var value; | ||
var numDone = 0; | ||
var overallErr; | ||
function checkDone() { | ||
if (++numDone === array.length) { | ||
callback(overallErr); | ||
} | ||
} | ||
if (Array.isArray(array) && array.length) { | ||
for (var i = 0; i < array.length; i++) { | ||
var task = array[i]; | ||
if (task) { | ||
key = Buffer.isBuffer(task.key) ? task.key : String(task.key); | ||
err = checkKeyValue(key, 'key'); | ||
if (err) { | ||
return nextTick(callback.bind(null, err)); | ||
overallErr = err; | ||
checkDone(); | ||
} else if (task.type === 'del') { | ||
self._del(task.key, options, checkDone); | ||
} else if (task.type === 'put') { | ||
value = Buffer.isBuffer(task.value) ? task.value : String(task.value); | ||
err = checkKeyValue(value, 'value'); | ||
if (err) { | ||
overallErr = err; | ||
checkDone(); | ||
} else { | ||
self._put(key, value, options, checkDone); | ||
} | ||
} | ||
this._put(key, value, options, noop); | ||
} else { | ||
checkDone(); | ||
} | ||
} | ||
} else { | ||
callback(); | ||
} | ||
} | ||
nextTick(callback); | ||
}); | ||
}; | ||
@@ -209,13 +280,3 @@ | ||
LD.destroy = function (name, callback) { | ||
try { | ||
Object.keys(localStorage) | ||
.forEach(function (key) { | ||
if (key.substring(0, name.length + 1) === (name + "!")) { | ||
localStorage.removeItem(key); | ||
} | ||
}); | ||
callback(); | ||
} catch (e) { | ||
// fail gracefully if no LocalStorage | ||
} | ||
LocalStorageCore.destroy(name, callback); | ||
}; | ||
@@ -222,0 +283,0 @@ |
@@ -9,84 +9,123 @@ 'use strict'; | ||
var utils = require('./utils'); | ||
var LocalStorageCore = require('./localstorage-core'); | ||
var TaskQueue = require('./taskqueue'); | ||
function LocalStorage(dbname) { | ||
this._keys = []; | ||
this._prefix = dbname.replace(/!/g, '!!') + '!'; // escape bangs in dbname | ||
var prefixLen = this._prefix.length; | ||
var i = -1; | ||
var len = window.localStorage.length; | ||
while (++i < len) { | ||
var fullKey = window.localStorage.key(i); | ||
if (fullKey.substring(0, prefixLen) === this._prefix) { | ||
this._keys.push(fullKey.substring(prefixLen)); | ||
} | ||
} | ||
this._keys.sort(); | ||
this._store = new LocalStorageCore(dbname); | ||
this._queue = new TaskQueue(); | ||
} | ||
//key: Returns the name of the key at the position specified. | ||
LocalStorage.prototype.key = function (keyindex) { | ||
return this._keys[keyindex]; | ||
LocalStorage.prototype.sequentialize = function (callback, fun) { | ||
this._queue.add(fun, callback); | ||
}; | ||
LocalStorage.prototype.init = function (callback) { | ||
var self = this; | ||
self.sequentialize(callback, function (callback) { | ||
self._store.getKeys(function (err, keys) { | ||
if (err) { | ||
return callback(err); | ||
} | ||
self._keys = keys; | ||
return callback(); | ||
}); | ||
}); | ||
}; | ||
// returns the key index if found, else the index where | ||
// the key should be inserted | ||
LocalStorage.prototype.indexOfKey = function(key) { | ||
return utils.sortedIndexOf(this._keys, key); | ||
// the key should be inserted. also returns the key | ||
// at that index | ||
LocalStorage.prototype.binarySearch = function (key, callback) { | ||
var self = this; | ||
self.sequentialize(callback, function (callback) { | ||
var index = utils.sortedIndexOf(self._keys, key); | ||
var foundKey = (index >= self._keys.length || index < 0) ? | ||
undefined : self._keys[index]; | ||
callback(null, {index: index, key: foundKey}); | ||
}); | ||
}; | ||
LocalStorage.prototype.getKeyAt = function (index, callback) { | ||
var self = this; | ||
self.sequentialize(callback, function (callback) { | ||
var foundKey = (index >= self._keys.length || index < 0) ? | ||
undefined : self._keys[index]; | ||
callback(null, foundKey); | ||
}); | ||
}; | ||
//setItem: Saves and item at the key provided. | ||
LocalStorage.prototype.setItem = function (key, value) { | ||
if (value instanceof ArrayBuffer) { | ||
value = arrayBuffPrefix + btoa(String.fromCharCode.apply(null, value)); | ||
} else if (value instanceof Uint8Array) { | ||
value = uintPrefix + btoa(String.fromCharCode.apply(null, value)); | ||
} | ||
LocalStorage.prototype.setItem = function (key, value, callback) { | ||
var self = this; | ||
self.sequentialize(callback, function (callback) { | ||
if (value instanceof ArrayBuffer) { | ||
value = arrayBuffPrefix + btoa(String.fromCharCode.apply(null, value)); | ||
} else if (value instanceof Uint8Array) { | ||
value = uintPrefix + btoa(String.fromCharCode.apply(null, value)); | ||
} | ||
var idx = utils.sortedIndexOf(this._keys, key); | ||
if (this._keys[idx] !== key) { | ||
this._keys.splice(idx, 0, key); | ||
} | ||
window.localStorage.setItem(this._prefix + key, value); | ||
var idx = utils.sortedIndexOf(self._keys, key); | ||
if (self._keys[idx] !== key) { | ||
self._keys.splice(idx, 0, key); | ||
} | ||
self._store.put(key, value, callback); | ||
}); | ||
}; | ||
//getItem: Returns the item identified by it's key. | ||
LocalStorage.prototype.getItem = function (key) { | ||
var value; | ||
var retval = window.localStorage.getItem(this._prefix + key); | ||
if (retval == null) { | ||
return undefined; | ||
} | ||
if (arrayBuffRegex.test(retval)) { | ||
value = retval.substring(arrayBuffPrefix.length); | ||
retval = new ArrayBuffer(atob(value).split('').map(function (c) { | ||
return c.charCodeAt(0); | ||
})); | ||
return retval; | ||
} else if (uintRegex.test(retval)) { | ||
value = retval.substring(uintPrefix.length); | ||
retval = new Uint8Array(atob(value).split('').map(function (c) { | ||
return c.charCodeAt(0); | ||
})); | ||
return retval; | ||
} | ||
return retval; | ||
LocalStorage.prototype.getItem = function (key, callback) { | ||
var self = this; | ||
self.sequentialize(callback, function (callback) { | ||
self._store.get(key, function (err, retval) { | ||
if (err) { | ||
return callback(err); | ||
} | ||
if (typeof retval === 'undefined' || retval === null) { | ||
// 'NotFound' error, consistent with LevelDOWN API | ||
return callback(new Error('NotFound')); | ||
} | ||
if (typeof retval !== 'undefined') { | ||
if (arrayBuffRegex.test(retval)) { | ||
retval = retval.substring(arrayBuffPrefix.length); | ||
retval = new ArrayBuffer(atob(retval).split('').map(function (c) { | ||
return c.charCodeAt(0); | ||
})); | ||
} else if (uintRegex.test(retval)) { | ||
retval = retval.substring(uintPrefix.length); | ||
retval = new Uint8Array(atob(retval).split('').map(function (c) { | ||
return c.charCodeAt(0); | ||
})); | ||
} | ||
} | ||
callback(null, retval); | ||
}); | ||
}); | ||
}; | ||
//removeItem: Removes the item identified by it's key. | ||
LocalStorage.prototype.removeItem = function (key) { | ||
var idx = utils.sortedIndexOf(this._keys, key); | ||
if (this._keys[idx] === key) { | ||
this._keys.splice(idx, 1); | ||
window.localStorage.removeItem(this._prefix + key); | ||
} | ||
LocalStorage.prototype.removeItem = function (key, callback) { | ||
var self = this; | ||
self.sequentialize(callback, function (callback) { | ||
var idx = utils.sortedIndexOf(self._keys, key); | ||
if (self._keys[idx] === key) { | ||
self._keys.splice(idx, 1); | ||
self._store.remove(key, function (err) { | ||
if (err) { | ||
return callback(err); | ||
} | ||
callback(); | ||
}); | ||
} else { | ||
callback(); | ||
} | ||
}); | ||
}; | ||
LocalStorage.prototype.length = function () { | ||
return this._keys.length; | ||
LocalStorage.prototype.length = function (callback) { | ||
var self = this; | ||
self.sequentialize(callback, function (callback) { | ||
callback(null, self._keys.length); | ||
}); | ||
}; | ||
exports.LocalStorage = LocalStorage; |
@@ -15,6 +15,9 @@ { | ||
], | ||
"version": "0.5.2", | ||
"version": "0.6.0", | ||
"main": "index.js", | ||
"dependencies": { | ||
"abstract-leveldown": "~0.12.0" | ||
"abstract-leveldown": "~0.12.0", | ||
"argsarray": "0.0.1", | ||
"inherits": "^2.0.1", | ||
"tiny-queue": "0.2.0" | ||
}, | ||
@@ -44,19 +47,13 @@ "devDependencies": { | ||
"browsers": [ | ||
"iexplore/10.0", | ||
"chrome/30.0", | ||
"chrome/31.0", | ||
"firefox/25.0", | ||
"firefox/24.0", | ||
"opera/15.0", | ||
"opera/16.0", | ||
"opera/17.0", | ||
"safari/5.0.5", | ||
"safari/5.1", | ||
"iexplore/8..latest", | ||
"chrome/22..latest", | ||
"chrome/canary", | ||
"firefox/24..latest", | ||
"firefox/nightly", | ||
"opera/15..latest", | ||
"opera/next", | ||
"chrome/canary", | ||
"iphone/6.0", | ||
"ipad/6.0", | ||
"safari/6.0", | ||
"android-browser/4.2" | ||
"safari/5.0.5..latest", | ||
"iphone/latest", | ||
"ipad/latest", | ||
"android-browser/latest" | ||
] | ||
@@ -63,0 +60,0 @@ }, |
@@ -25,2 +25,9 @@ # localstorage-down | ||
## Browser support | ||
Basically we support [any browser that has localStorage](http://caniuse.com/namevalue-storage), but since we also rely on an ES5 environment due to dependencies from abstract-leveldown, in practice you will need the following shims in order to work correctly on all browsers (e.g. IE 8/9): | ||
* [typedarray](https://github.com/substack/typedarray) for binary storage | ||
* [es5-shim](https://github.com/es-shims/es5-shim) for just about everything | ||
## Example | ||
@@ -27,0 +34,0 @@ |
@@ -6,5 +6,3 @@ 'use strict'; | ||
var testCommon = require('./testCommon'); | ||
var testBuffer = new Buffer(new Uint8Array('hello'.split('').map(function (c) { | ||
return c.charCodeAt(0); | ||
}))); | ||
var testBuffer = new Buffer('hello'); | ||
@@ -30,43 +28,1 @@ require('abstract-leveldown/abstract/leveldown-test').args(localstorage, tape); | ||
// we need typed arrays | ||
function TypedArray(arg1) { | ||
var result; | ||
if (typeof arg1 === "number") { | ||
result = new Array(arg1); | ||
for (var i = 0; i < arg1; ++i) { | ||
result[i] = 0; | ||
} | ||
} else { | ||
result = arg1.slice(0); | ||
result.buffer = result; | ||
result.byteLength = result.length; | ||
result.subarray = function (start, end) { | ||
return result.slice(start, end); | ||
}; | ||
result.set = function (array, offset) { | ||
if (arguments.length < 2) { | ||
offset = 0; | ||
} | ||
for (var i = 0, n = array.length; i < n; ++i, ++offset) { | ||
result[offset] = array[i] & 0xFF; | ||
} | ||
}; | ||
} | ||
if (typeof arg1 === "object" && arg1.buffer) { | ||
result.buffer = arg1.buffer; | ||
} | ||
return result; | ||
} | ||
if (!window.Uint8Array) { | ||
window.Uint8Array = TypedArray; | ||
window.Uint32Array = TypedArray; | ||
window.Int32Array = TypedArray; | ||
} | ||
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
61626
24
653
102
4
+ Addedargsarray@0.0.1
+ Addedinherits@^2.0.1
+ Addedtiny-queue@0.2.0
+ Addedargsarray@0.0.1(transitive)
+ Addedinherits@2.0.4(transitive)
+ Addedtiny-queue@0.2.0(transitive)