export default class LocalStorageInterface{ /* (1) Constructs a localStorage Interface * * @_prefix localStorage Interface Prefix (prefix) * @_ttl Seconds default valid time for stored data * @_session Whether to use sessionStorage (default is localStorage) * ---------------------------------------------------------*/ constructor(_prefix, _ttl, _session=false){ /* (1) Initialise private attributes */ this._prefix = 'root'; this._ttl = null; this._driver = !!_session ? sessionStorage : localStorage; /* (2) Initialise public attributes */ this.keys = []; /* (3) Set given _prefix if valid */ if( typeof _prefix === 'string' ) this._prefix = _prefix; /* (4) Set given _ttl if valid */ if( !isNaN(_ttl) ) this._ttl = _ttl; /* (5) Synchronize keys */ this.synchronise(); } /* (1) Synchronise keys from this._driver * ---------------------------------------------------------*/ synchronise(){ // 1. Get this._driver key list let keys = Object.keys(this._driver); // 2. Local copy of prefix let prefix = `${this._prefix}.`; // 3. Only keep prefixed keys let prefixed = keys.filter( (k) => ( k.substr(0, prefix.length) === prefix ) ); // 4. Remove prefix let unprefixed = prefixed.map( (k) => k.substr(prefix.length) ); // X. Update keys this.keys = unprefixed; } /* (2) Store data * * @_name Object key * @_data Object key * @_ttl [OPT] Seconds valid time for this data * ---------------------------------------------------------*/ push(_name, _data, _ttl=null){ /* (1) Manage argument ---------------------------------------------------------*/ /* (1) Invalid _name type */ if( typeof _name !== 'string' ) return false; /* (2) Default value for _ttl */ let ttl = isNaN(_ttl) || _ttl === null ? this._ttl : parseInt(_ttl); /* (2) Store data ---------------------------------------------------------*/ /* (1) Build storage object */ let storage_object = { expires: ttl ? new Date().getTime() + ttl*1000 : null, data: _data }; /* (2) Store storage object */ this._driver.setItem(`${this._prefix}.${_name}`, JSON.stringify(storage_object)); /* (3) Synchronize keys */ this.keys.push(_name); /* (4) Return status */ return true; } /* (3) Fetch data * * @_name Object key * * @return outName Object data (NULL on error or not found) * ---------------------------------------------------------*/ fetch(_name){ /* (1) Manage argument ---------------------------------------------------------*/ /* (1) Invalid _name type */ if( typeof _name !== 'string' ) return null; /* (2) invalid key */ if( this.keys.indexOf(_name) < 0 ) return null; /* (2) Fetch data ---------------------------------------------------------*/ /* (1) Try to get data */ let fetched = this._driver.getItem(`${this._prefix}.${_name}`); /* (2) Try to parse */ try{ var storage_object = JSON.parse(fetched); /* (3) If cannot parse -> remove */ }catch(e){ this.pop(_name); return null; } /* (4) If not valid anymore -> delete + return NULL */ if( !isNaN(storage_object.expires) && storage_object.expires < new Date().getTime() ){ this.pop(_name); return null; } /* (5) Return data */ return storage_object.data; } /* (4) Remove data * * @_name Object key * ---------------------------------------------------------*/ pop(_name){ /* (1) Invalid _name type */ if( typeof _name !== 'string' ) return null; /* (2) invalid key */ if( this.keys.indexOf(_name) < 0 ) return null; /* (3) Remove from this._driver */ this._driver.removeItem(`${this._prefix}.${_name}`); /* (4) Update keys */ this.keys = this.keys.filter( (k) => (k!==_name) ); } }