Skip to content

Commit

Permalink
Update listen/unlisten code for merge
Browse files Browse the repository at this point in the history
  • Loading branch information
danielstjules committed Jun 12, 2017
1 parent 360b896 commit e69f8b3
Show file tree
Hide file tree
Showing 4 changed files with 138 additions and 173 deletions.
54 changes: 27 additions & 27 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@ Features an API using ES6 promises.
* [CrossStorageClient.prototype.del(key1, \[key2\], \[...\])](#crossstorageclientprototypedelkey1-key2-)
* [CrossStorageClient.prototype.getKeys()](#crossstorageclientprototypegetkeys)
* [CrossStorageClient.prototype.clear()](#crossstorageclientprototypeclear)
* [CrossStorageClient.prototype.close()](#crossstorageclientprototypeclose)
* [CrossStorageClient.prototype.listen(callback)](#crossstorageclientprototypelisten)
* [CrossStorageClient.prototype.unlisten(key)](#crossstorageclientprototypeunlisten)
* [CrossStorageClient.prototype.close()](#crossstorageclientprototypeclose)
* [Compatibility](#compatibility)
* [Compression](#compression)
* [Building](#building)
Expand Down Expand Up @@ -233,47 +233,47 @@ storage.onConnect().then(function() {
});
```

#### CrossStorageClient.prototype.close()
#### CrossStorageClient.prototype.listen(fn)

Deletes the iframe and sets the connected state to false. The client can
no longer be used after being invoked.
Adds an event listener to the storage event in the hub. The callback will
be invoked on any storage event not originating from that client. The
callback will be invoked with an object containing the following keys taken
from the original event: `key`, `newValue`, `oldValue` and `url`. Returns a
promise that resolves to a listener id that can be used to unregister the
listener.

``` javascript
storage.onConnect().then(function() {
return storage.set('key1', 'key2');
}).catch(function(err) {
// Handle error
}).then(function() {
storage.close();
return storage.listen(function(event) {
console.log(event);
});
}).then(function(id) {
// id can be passed to storage.unlisten
});
```

#### CrossStorageClient.prototype.listen(callback)

Adds an event listener to the `storage` event in the hub. All `storage` events
will be sent to the client and used to call the given callback.
#### CrossStorageClient.prototype.unlisten(id)

The callback will be called on each `storage` event, with an object with the
keys `key`, `newValue`, `oldValue` and `url` taken from the original event.
Removes the registered listener with the supplied id. Returns a promise
that resolves on completion.

``` javascript
var storageEventListenerKey;
storage.onConnect().then(function() {
return storage.listen(console.log);
}).then(function(key) {
storageEventListenerKey = key
});
storage.unlisten(id);
```

#### CrossStorageClient.prototype.unlisten(eventKey)
#### CrossStorageClient.prototype.close()

Removes the storage event listener.

The client will ignore any events as soon as this is called. Returns a promise
that is settled on successful event listener removal from the hub.
Deletes the iframe and sets the connected state to false. The client can
no longer be used after being invoked.

``` javascript
storage.unlisten(storageEventListenerKey);
storage.onConnect().then(function() {
return storage.set('key1', 'key2');
}).catch(function(err) {
// Handle error
}).then(function() {
storage.close();
});
```

## Compatibility
Expand Down
82 changes: 39 additions & 43 deletions lib/client.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,9 @@
this._count = 0;
this._timeout = opts.timeout || 5000;
this._listener = null;
this._storageEventListeners = {};
this._storageEventListenerCount = 0;

this._storageListeners = {};
this._storageListenerCount = 0;

this._installListener();

Expand Down Expand Up @@ -195,44 +196,6 @@
return this._request('get', {keys: args});
};

/**
* Accepts a callback which will be called on `storage` events from the hub.
*
* The callback will be called on changes to the hub's storage (trigger from
* other documents than the hub). It will be called with an object with
* the keys `key`, `newValue`, `oldValue` and `url`, as defined by the `storage`
* event in the hub.
*
* Returns a promise that is settled on success (in adding the event listener),
* in which case it is fullfilled with a key that can be used to remove the
* listener. On failure, it is rejected with the corresponding error message.
*
* @param {function} callback Function to be called on storage changes
* @returns {Promise} A promise that is settled on hub response or timeout
*/
CrossStorageClient.prototype.listen = function(callback) {
this._storageEventListenerCount++;
var eventKey = this._id + ":" + this._storageEventListenerCount;
this._storageEventListeners[eventKey] = callback;
return this._request('listen', {eventKey: eventKey}).then(function () {
return eventKey
});
};

/**
* Removes the storage event listener.
*
* The client will ignore any events as soon as this is called. Returns a promise
* that is settled on successful event listener removal from the hub.
*
* @param {string} eventKey The key returned initiating the listener with `listen`
* @returns {Promise} A promise that is settled on hub response or timeout
*/
CrossStorageClient.prototype.unlisten = function(eventKey) {
delete this._storageEventListeners[eventKey];
return this._request('unlisten', {eventKey: eventKey});
};

/**
* Accepts one or more keys for deletion. Returns a promise that is settled on
* hub response or timeout.
Expand Down Expand Up @@ -266,6 +229,39 @@
return this._request('getKeys');
};

/**
* Adds an event listener to the storage event in the hub. The callback will
* be invoked on any storage event not originating from that client. The
* callback will be invoked with an object containing the following keys taken
* from the original event: `key`, `newValue`, `oldValue` and `url`. Returns a
* promise that resolves to a listener id that can be used to unregister the
* listener.
*
* @param {function} fn Callback to invoke on storage event
* @returns {Promise} A promise that is settled on hub response or timeout
*/
CrossStorageClient.prototype.listen = function(fn) {
this._storageListenerCount++;
var id = this._id + ":" + this._storageListenerCount;
this._storageListeners[id] = fn;
return this._request('listen', {listenerId: id}).then(function() {
return id;
});
};

/**
* Removes the registered listener with the supplied id. Returns a promise
* that resolves on completion.
*
* @param {string} id The id of the listener to unregister
* @returns {Promise} A promise that is settled on hub response or timeout
*/
CrossStorageClient.prototype.unlisten = function(id) {
delete this._storageListeners[id];
return this._request('unlisten', {listenerId: id});
};


/**
* Deletes the iframe and sets the connected state to false. The client can
* no longer be used after being invoked.
Expand Down Expand Up @@ -347,9 +343,9 @@
return;
}

if(response.type === 'event') {
if (response.eventKey in client._storageEventListeners) {
client._storageEventListeners[response.eventKey](response.eventData);
if (response.event) {
if (client._storageListeners[response.listenerId]) {
client._storageListeners[response.listenerId](response.event);
}
return;
}
Expand Down
45 changes: 25 additions & 20 deletions lib/hub.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
}

CrossStorageHub._permissions = permissions || [];
CrossStorageHub._eventListeners = {};
CrossStorageHub._storageListeners = {};
CrossStorageHub._installListener();
window.parent.postMessage('cross-storage:ready', '*');
};
Expand Down Expand Up @@ -121,16 +121,21 @@
/**
* Returns a boolean indicating whether or not the requested method is
* permitted for the given origin. The argument passed to method is expected
* to be one of 'get', 'set', 'del', 'clear', 'listen' or 'getKeys'.
* to be one of 'get', 'set', 'del', 'clear', 'getKeys', 'listen', or
* 'unlisten'.
*
* @param {string} origin The origin for which to determine permissions
* @param {string} method Requested action
* @returns {bool} Whether or not the request is permitted
*/
CrossStorageHub._permitted = function(origin, method) {
var available, i, entry, match;
if (method==='unlisten') method = 'listen';
available = ['get', 'set', 'listen', 'del', 'clear', 'getKeys'];
available = ['get', 'set', 'listen', 'del', 'clear', 'getKeys', 'listen'];

if (method === 'unlisten') {
method = 'listen';
}

if (!CrossStorageHub._inArray(method, available)) {
return false;
}
Expand Down Expand Up @@ -187,54 +192,54 @@
};

/**
* Adds an event listener to `storage` events which sends all events to the client with the given eventKey
* Listens to storage events, sending them to the client.
*
* @param {object} params An object with an eventKey
* @param {object} params An object with a listener id
*/
CrossStorageHub._listen = function(params) {
if (params.eventKey in CrossStorageHub._eventListeners) {
throw new Error("Can't reuse eventKeys")
if (params.listenerId in CrossStorageHub._storageListeners) {
return;
}

var handler = function(event) {
if (event.storageArea != window.localStorage) return;
if (event.storageArea !== window.localStorage) return;

var data = {
type: 'event',
eventKey: params.eventKey,
eventData: {
listenerId: params.listenerId,
event: {
key: event.key,
newValue: event.newValue,
oldValue: event.oldValue,
url: event.url
// storageArea, ignored because we only use localStorage
}
};

window.parent.postMessage(JSON.stringify(data), '*');
};

// Support IE8 with attachEvent
CrossStorageHub._storageListeners[params.listenerId] = handler;

if (window.addEventListener) {
window.addEventListener('storage', handler, false);
} else {
window.attachEvent('onstorage', handler);
}
CrossStorageHub._eventListeners[params.eventKey] = handler
};

/**
* Removes an event listener with the given eventKey
* Removes an event listener with the given id
*
* @param {object} params An object with an eventKey
* @param {object} params An object with an id
*/
CrossStorageHub._unlisten = function(params) {
var handler = CrossStorageHub._eventListeners[params.eventKey];
var handler = CrossStorageHub._storageListeners[params.listenerId];
CrossStorageHub._storageListeners[params.listenerId] = null;

// Support IE8 with attachEvent
if (window.removeEventListener) {
window.removeEventListener('storage', handler, false);
} else {
window.detachEvent('onstorage', handler);
}
CrossStorageHub._eventListeners[params.eventKey] = null
};

/**
Expand Down
Loading

0 comments on commit e69f8b3

Please sign in to comment.