From d712df012c0fe3f794e49e82ea128ac2bdaaff57 Mon Sep 17 00:00:00 2001
From: Matthias Seemann
Date: Thu, 3 Dec 2015 17:09:26 +0100
Subject: [PATCH 1/2] fixed that items with store API key names are considered
by the key() method
---
.gitignore | 8 ++++++++
dist/memorystorage.min.js | 4 ++--
dist/memorystorage.min.js.map | 2 +-
dist/memorystorage.umd.js | 4 ++--
package.json | 4 ++--
src/memorystorage.js | 4 ++--
tests/test.js | 4 +++-
7 files changed, 20 insertions(+), 10 deletions(-)
diff --git a/.gitignore b/.gitignore
index 3c3629e..09ad9b7 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,9 @@
node_modules
+
+# OS generated files #
+.DS_Store
+.DS_Store?
+.Trashes
+
+# PhpStorm project files #
+.idea
\ No newline at end of file
diff --git a/dist/memorystorage.min.js b/dist/memorystorage.min.js
index 47881a0..89d1494 100644
--- a/dist/memorystorage.min.js
+++ b/dist/memorystorage.min.js
@@ -1,3 +1,3 @@
-/*! [memorystorage 0.9.10](http://download.github.io/memorystorage) Copyright 2015 by [Stijn de Witt](http://StijnDeWitt.com). Some rights reserved. License: [CC-BY-4.0](https://creativecommons.org/licenses/by/4.0/) */
-!function(u,m,d){"function"==typeof define&&define.amd?define("memorystorage",[],function(){return d()}):"object"==typeof exports?module.exports=d():u[m]=d()}(this,"MemoryStorage",function(){"use strict";function MemoryStorage(f){f=f||"global";var g=e[f];if(g)return g;if(!(this instanceof MemoryStorage))return new MemoryStorage(f);g=e[f]=this;var h={};return Object.defineProperty(g,c,{enumerable:!1,get:function(){return h}}),Object.defineProperty(g,"id",{enumerable:!0,get:function(){return f}}),Object.defineProperty(g,"length",{enumerable:!0,get:function(){return Object.keys(this).length+Object.keys(this[c]).length-b}}),g.getItem=function(b){return b in a?this[c][b]:this[b]},g.setItem=function(b,e){b in a?this[c][b]=e:this[b]=e},g.removeItem=function(b){b in a?delete this[c][b]:delete this[b]},g.key=function(b){var e=Object.keys(this).concat(Object.keys(this[c]));return e=e.filter(function(b){return!(b in a)}),b>=0&&b=0&&b= 0 && idx < keys.length ? keys[idx] : null;
};
result.clear = function MemoryStorage_clear() {
diff --git a/package.json b/package.json
index ec041ef..93368f2 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "memorystorage",
- "version": "0.9.10",
+ "version": "0.9.11",
"description": "Memory-backed implementation of the Web Storage API",
"src": "src/memorystorage.js",
"main": "dist/memorystorage.umd.js",
@@ -40,7 +40,7 @@
},
"homepage": "http://download.github.io/memorystorage",
"devDependencies": {
- "grunt": "~0.4.5",
+ "grunt": "^0.4.5",
"grunt-contrib-jshint": "~0.10.0",
"grunt-contrib-uglify": "~0.6.0",
"grunt-jsdoc": "~0.6.7",
diff --git a/src/memorystorage.js b/src/memorystorage.js
index 13a044f..c7cce4b 100644
--- a/src/memorystorage.js
+++ b/src/memorystorage.js
@@ -74,8 +74,8 @@ function MemoryStorage(id) {// jshint ignore:line
else {delete this[key];}
};
result.key = function MemoryStorage_key(idx) {
- var keys = Object.keys(this).concat(Object.keys(this[CLOAK]));
- keys = keys.filter(function(x){return !(x in API);});
+ var keys = Object.keys(this).filter(function(x){return !(x in API);});
+ keys = keys.concat(Object.keys(this[CLOAK]));
return idx >= 0 && idx < keys.length ? keys[idx] : null;
};
result.clear = function MemoryStorage_clear() {
diff --git a/tests/test.js b/tests/test.js
index 19d5e46..3364b1f 100644
--- a/tests/test.js
+++ b/tests/test.js
@@ -34,9 +34,12 @@ QUnit.test("W3C Web Storage API Compliance Test", function( assert ) {
assert.ok(store.length===2, "double removal has no effect");
assert.ok(store.getItem('test1')===undefined, "get removed item returns undefined");
+ store.clear();
store.setItem('getItem', 'test');
assert.ok(typeof store.getItem === 'function', "store API methods cannot be overwritten.");
assert.ok(store.getItem('getItem') === 'test', "getItem successfully retrieves item with API name.");
+ assert.ok(store.length===1, 'items with store API key names should be counted in the length property');
+ assert.ok(store.key(0)==='getItem', 'items with store API key names should be enumerable with key()');
store.removeItem('getItem');
assert.ok(store.getItem('getItem') === undefined, "After removal of item with API name, getItem returns undefined.");
@@ -50,7 +53,6 @@ QUnit.test("W3C Web Storage API Compliance Test", function( assert ) {
assert.ok(glob.key(0)===null, "global keys are removed correctly");
assert.ok(glob.getItem('glob0')===undefined, "global values are removed correctly");
- store.clear();
assert.ok(store.length===0, "store is cleared");
assert.ok(store.key(0)===null, "no keys in cleared store");
assert.ok(store.getItem('test0')===undefined, "no values in cleared store");
From f989601f0cec1f1f16738200c656300aaddebe56 Mon Sep 17 00:00:00 2001
From: Matthias Seemann
Date: Thu, 3 Dec 2015 22:05:40 +0100
Subject: [PATCH 2/2] Added support for item key enumeration with Object.keys()
on JS hosts which support ES6 Proxies, Warning: In JS environments without
ES6 Proxies Object.keys(memoryStorage) retains the old behaviour in returning
all API keys and all item keys
---
.jshintrc | 1 +
dist/memorystorage.min.js | 4 ++--
dist/memorystorage.min.js.map | 2 +-
dist/memorystorage.umd.js | 35 ++++++++++++++++++++++++++++++-----
package.json | 2 +-
src/memorystorage.js | 35 ++++++++++++++++++++++++++++++-----
tests/index.html | 1 +
tests/test.js | 14 +++++++++++++-
8 files changed, 79 insertions(+), 15 deletions(-)
diff --git a/.jshintrc b/.jshintrc
index 307db79..3f8ac31 100644
--- a/.jshintrc
+++ b/.jshintrc
@@ -16,6 +16,7 @@
"browser": true,
"shadow": true,
"devel": true,
+ "esnext": true,
"globals": {
"define": false,
diff --git a/dist/memorystorage.min.js b/dist/memorystorage.min.js
index 89d1494..639f864 100644
--- a/dist/memorystorage.min.js
+++ b/dist/memorystorage.min.js
@@ -1,3 +1,3 @@
-/*! [memorystorage 0.9.11](http://download.github.io/memorystorage) Copyright 2015 by [Stijn de Witt](http://StijnDeWitt.com). Some rights reserved. License: [CC-BY-4.0](https://creativecommons.org/licenses/by/4.0/) */
-!function(u,m,d){"function"==typeof define&&define.amd?define("memorystorage",[],function(){return d()}):"object"==typeof exports?module.exports=d():u[m]=d()}(this,"MemoryStorage",function(){"use strict";function MemoryStorage(f){f=f||"global";var g=e[f];if(g)return g;if(!(this instanceof MemoryStorage))return new MemoryStorage(f);g=e[f]=this;var h={};return Object.defineProperty(g,c,{enumerable:!1,get:function(){return h}}),Object.defineProperty(g,"id",{enumerable:!0,get:function(){return f}}),Object.defineProperty(g,"length",{enumerable:!0,get:function(){return Object.keys(this).length+Object.keys(this[c]).length-b}}),g.getItem=function(b){return b in a?this[c][b]:this[b]},g.setItem=function(b,e){b in a?this[c][b]=e:this[b]=e},g.removeItem=function(b){b in a?delete this[c][b]:delete this[b]},g.key=function(b){var e=Object.keys(this).filter(function(b){return!(b in a)});return e=e.concat(Object.keys(this[c])),b>=0&&b=0&&a}
+ */
+ function enumerableKeys(){
+ var keys = Object.keys(result).filter(function(x){return !(x in API);});
+ return keys.concat(Object.keys(cloaked));
+ }
+
// Allow client code to read the id
Object.defineProperty(result, 'id', {
enumerable: true,
+ configurable: true,
get: function(){return id;}
});
// Create the length property
Object.defineProperty(result, 'length', {
enumerable: true,
+ configurable: true,
get: function(){
- return Object.keys(this).length + Object.keys(this[CLOAK]).length - API_LENGTH;
+ return enumerableKeys().length;
}
});
// Create API methods
@@ -79,9 +90,13 @@ function MemoryStorage(id) {// jshint ignore:line
if (key in API) {delete this[CLOAK][key];}
else {delete this[key];}
};
+ /**
+ * Needed to enumerate over all items in the collection
+ * @param {Number} idx - the index
+ * @returns {null|string} - the name of the nth key in the storage
+ */
result.key = function MemoryStorage_key(idx) {
- var keys = Object.keys(this).filter(function(x){return !(x in API);});
- keys = keys.concat(Object.keys(this[CLOAK]));
+ var keys = enumerableKeys();
return idx >= 0 && idx < keys.length ? keys[idx] : null;
};
result.clear = function MemoryStorage_clear() {
@@ -94,7 +109,17 @@ function MemoryStorage(id) {// jshint ignore:line
delete this[CLOAK][key];
}
};
- return result;
+
+ if (typeof Proxy === 'undefined')
+ {
+ return result;
+ }
+ // ES6 Proxy to support Object.keys() on a MemoryStorage object
+ return new Proxy(result, {
+ ownKeys: function() {
+ return enumerableKeys();
+ }
+ });
}
diff --git a/package.json b/package.json
index 93368f2..6fdefc7 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "memorystorage",
- "version": "0.9.11",
+ "version": "0.9.12",
"description": "Memory-backed implementation of the Web Storage API",
"src": "src/memorystorage.js",
"main": "dist/memorystorage.umd.js",
diff --git a/src/memorystorage.js b/src/memorystorage.js
index c7cce4b..d258f3b 100644
--- a/src/memorystorage.js
+++ b/src/memorystorage.js
@@ -6,7 +6,6 @@
*/
// API methods and properties will be cloaked
var API = {'clear':1, 'getItem':1, 'id':1, 'key':1, 'length':1, 'removeItem':1, 'setItem':1},
- API_LENGTH = Object.keys(API).length,
CLOAK = '__memorystorage_cloaked_items__';
// Used to store all memorystorage objects
@@ -47,18 +46,30 @@ function MemoryStorage(id) {// jshint ignore:line
var cloaked = {};
Object.defineProperty(result, CLOAK, {
enumerable: false,
+ configurable: true,
get: function(){return cloaked;}
});
+ /**
+ * private method to find all enumerable keys
+ * @returns {Array.}
+ */
+ function enumerableKeys(){
+ var keys = Object.keys(result).filter(function(x){return !(x in API);});
+ return keys.concat(Object.keys(cloaked));
+ }
+
// Allow client code to read the id
Object.defineProperty(result, 'id', {
enumerable: true,
+ configurable: true,
get: function(){return id;}
});
// Create the length property
Object.defineProperty(result, 'length', {
enumerable: true,
+ configurable: true,
get: function(){
- return Object.keys(this).length + Object.keys(this[CLOAK]).length - API_LENGTH;
+ return enumerableKeys().length;
}
});
// Create API methods
@@ -73,9 +84,13 @@ function MemoryStorage(id) {// jshint ignore:line
if (key in API) {delete this[CLOAK][key];}
else {delete this[key];}
};
+ /**
+ * Needed to enumerate over all items in the collection
+ * @param {Number} idx - the index
+ * @returns {null|string} - the name of the nth key in the storage
+ */
result.key = function MemoryStorage_key(idx) {
- var keys = Object.keys(this).filter(function(x){return !(x in API);});
- keys = keys.concat(Object.keys(this[CLOAK]));
+ var keys = enumerableKeys();
return idx >= 0 && idx < keys.length ? keys[idx] : null;
};
result.clear = function MemoryStorage_clear() {
@@ -88,5 +103,15 @@ function MemoryStorage(id) {// jshint ignore:line
delete this[CLOAK][key];
}
};
- return result;
+
+ if (typeof Proxy === 'undefined')
+ {
+ return result;
+ }
+ // ES6 Proxy to support Object.keys() on a MemoryStorage object
+ return new Proxy(result, {
+ ownKeys: function() {
+ return enumerableKeys();
+ }
+ });
}
diff --git a/tests/index.html b/tests/index.html
index 92b52ee..64e1c1c 100644
--- a/tests/index.html
+++ b/tests/index.html
@@ -3,6 +3,7 @@
MemoryStorage Tests
+
Restart | Next Test
diff --git a/tests/test.js b/tests/test.js
index 3364b1f..a8203ca 100644
--- a/tests/test.js
+++ b/tests/test.js
@@ -15,7 +15,14 @@ QUnit.test("W3C Web Storage API Compliance Test", function( assert ) {
assert.ok(store.length===2, 'value added correctly with index operators');
store.setItem('test2', 'data2');
assert.ok(store.length===3, 'three items added to store');
- assert.ok(Object.keys(store).length == (7+3), "store has 10 enumerable properties (id, 6 api methods + 3 stored items)");
+ if (typeof Proxy === "undefined") {
+ assert.ok(Object.keys(store).length == (7+3), "store has 10 enumerable properties (id, 6 api methods + 3 stored items)");
+ }
+ else {
+ assert.ok(Object.keys(store).length === 3, "store has 3 enumerable properties (no api methods + 3 stored items)");
+ assert.ok(Object.keys(store).sort().join(',') === "test0,test1,test2",
+ "keys are enumerable with Object.keys()");
+ }
assert.ok(store.getItem('test1')==='data1' && store.getItem('test2')==='data2', "retrieved values matches stored values");
var keyOrderBefore = '';
for (var i=0; i