import ns from '../namespace';
import GenericError from '../error/GenericError';
import Storage from './Storage';
import Window from '../window/Window';
ns.namespace('ima.storage');
/**
* Implementation of the {@codelink Storage} interface that relies on the
* native {@code sessionStorage} DOM storage for storing its entries.
*/
export default class SessionStorage extends Storage {
static get $dependencies() {
return [Window];
}
/**
* Initializes the session storage.
* @param {Window} window
*/
constructor(window) {
super();
/**
* The DOM storage providing the actual storage of the entries.
*
* @type {Storage}
*/
this._storage = window.getWindow().sessionStorage;
}
/**
* @inheritdoc
*/
init() {
return this;
}
/**
* @inheritdoc
*/
has(key) {
return !!this._storage.getItem(key);
}
/**
* @inheritdoc
*/
get(key) {
try {
return JSON.parse(this._storage.getItem(key)).value;
} catch (error) {
throw new GenericError(
'ima.storage.SessionStorage.get: Failed to parse a session ' +
`storage item value identified by the key ${key}: ` +
error.message
);
}
}
/**
* @inheritdoc
*/
set(key, value) {
try {
this._storage.setItem(
key,
JSON.stringify({
created: Date.now(),
value
})
);
} catch (error) {
let storage = this._storage;
let isItemTooBig =
storage.length === 0 ||
(storage.length === 1 && storage.key(0) === key);
if (isItemTooBig) {
throw error;
}
this._deleteOldestEntry();
this.set(key, value);
}
return this;
}
/**
* @inheritdoc
*/
delete(key) {
this._storage.removeItem(key);
return this;
}
/**
* @inheritdoc
*/
clear() {
this._storage.clear();
return this;
}
/**
* @inheritdoc
*/
keys() {
return new StorageIterator(this._storage);
}
/**
* @override
*/
size() {
return this._storage.length;
}
/**
* Deletes the oldest entry in this storage.
*/
_deleteOldestEntry() {
let oldestEntry = {
key: null,
created: Date.now() + 1
};
for (let key of this.keys()) {
let value = JSON.parse(this._storage.getItem(key));
if (value.created < oldestEntry.created) {
oldestEntry = {
key,
created: value.created
};
}
}
if (typeof oldestEntry.key === 'string') {
this.delete(oldestEntry.key);
}
}
}
/**
* Implementation of the iterator protocol and the iterable protocol for DOM
* storage keys.
*
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols
*/
class StorageIterator {
/**
* Initializes the DOM storage iterator.
*
* @param {Storage} storage The DOM storage to iterate through.
*/
constructor(storage) {
/**
* The DOM storage being iterated.
*
* @type {Storage}
*/
this._storage = storage;
/**
* The current index of the DOM storage key this iterator will return
* next.
*
* @type {number}
*/
this._currentKeyIndex = 0;
}
/**
* Iterates to the next item. This method implements the iterator protocol.
*
* @return {{done: boolean, value: (undefined|string)}} The next value in
* the sequence and whether the iterator is done iterating through
* the values.
*/
next() {
if (this._currentKeyIndex >= this._storage.length) {
return {
done: true,
value: undefined
};
}
let key = this._storage.key(this._currentKeyIndex);
this._currentKeyIndex++;
return {
done: false,
value: key
};
}
/**
* Returns the iterator for this object (this iterator). This method
* implements the iterable protocol and provides compatibility with the
* {@code for..of} loops.
*
* @return {StorageIterator} This iterator.
*/
[Symbol.iterator]() {
return this;
}
}
ns.ima.storage.SessionStorage = SessionStorage;
|